I am writing an unit test in Spock and trying to stub RestTemplate.postForEntity() but seeing a weird issue where the stubs only works when I add a breakpoint and evaluate the expression. It doesnt work and calls the postForEntity() method when run normally.
Here is my implementation.
Test:
def "AuthTokenRequest"() {
given:
def url = "https://authEndpoint"
def map = new LinkedMultiValueMap<>()
RestTemplate restTemplate = GroovySpy(global: true)
restTemplate.postForEntity(*_) >> new ResponseEntity(new AuthResponse(), HttpStatus.OK)
when:
def responseEntity = client.authTokenRequest(url, map)
then:
responseEntity == response
}
Method making the HTTP request:
public AuthResponse authTokenRequest(String url, MultiValueMap map) {
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<AuthResponse> responseEntity = restTemplate.postForEntity(url, createAuthEntity(map), AuthResponse.class);
return responseEntity.getBody()?:null
}
When I run this test, I get a java.net.UnknownHostException exception because https://authEndpoint doesnt point anywhere which also proves that postEntity() is not being stubbed.
When I put a breakpoint on ResponseEntity<AuthResponse> responseEntity = restTemplate.postForEntity(url, createAuthEntity(map), AuthResponse.class); and evaluate the expression, I get the stubbed response.
Am I doing this wrong?
Related
While doing some searches on SO I came across this piece of code to extract the "appUrl" from a URL:
public static String getAppUrl(HttpServletRequest request)
{
String requestURL = request.getRequestURL().toString();
String servletPath = request.getServletPath();
return requestURL.substring(0, requestURL.indexOf(servletPath));
}
My question is how does one unit test something like this? The key problem is how to create an instance of HttpServletRequest for unit testing?
Fwiw I tried some googling and most of the responses center around mocking the class. But if I mock the class so that getRequestURL returns what I want it to return (taking an example since mocking essentially overrides some methods to return canned values), then I am not really testing the code at that point. I also tried the httpunit library but that also does not help.
I use mockito and here is the block of code in the test method I use to mock it up:
public class TestLogin {
#Test
public void testGetMethod() throws IOException {
// Mock up HttpSession and insert it into mocked up HttpServletRequest
HttpSession session = mock(HttpSession.class);
given(session.getId()).willReturn("sessionid");
// Mock up HttpServletRequest
HttpServletRequest request = mock(HttpServletRequest.class);
given(request.getSession()).willReturn(session);
given(request.getSession(true)).willReturn(session);
HashMap<String,String[]> params = new HashMap<>();
given(request.getParameterMap()).willReturn(params);
// Mock up HttpServletResponse
HttpServletResponse response = mock(HttpServletResponse.class);
PrintWriter writer = mock(PrintWriter.class);
given(response.getWriter()).willReturn(writer);
.....
Hope that helps, I use this to test methods that require servlet objects to work.
I have a controller class with this code:
List<MultipartFile> files = []
List<String> convertedContents = []
def convertedFiles = [:]
try {
params.myFile.each {
if (((MultipartFile) it.value).empty) {
throw new NoUploadedFileException('Break .each closure due to empty input.')
}
files.add((MultipartFile) it.value)
}
} catch (NoUploadedFileException e) {
redirect uri: request.getHeader('referer')
return
}
convertedContents = converterService.convertToBase64(files)
(code omitted)
I also have a test:
def "sampleTest"() {
when:
controller.sendFax()
then:
thrown(NoUploadedFileException)
response.redirectedUrl == 'index.gsp'
}
What I'm trying to test is that my Controller would throw a "NoUploadedFileException" when no file are uploaded and the submit button is clicked.
This is the error:
Running 1 unit test... 1 of 1
| Failure: sampleTest(com.synacy.HomeControllerSpec)
| Expected exception com.synacy.NoUploadedFileException, but got
java.lang.NullPointerException
at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:79)
at com.synacy.HomeControllerSpec.throws NoUploadedFileException and returns to the
same page when no file is uploaded(HomeControllerSpec.groovy:36)
Caused by: java.lang.NullPointerException: Cannot invoke method convertToBase64() on
null object
at com.synacy.HomeController.sendFax(HomeController.groovy:43)
at com.synacy.HomeControllerSpec.sampleTest(HomeControllerSpec.groovy:33)
It seems that it isn't going through the try-catch block, or if it is, the implementation is not working.
For a Unit test, in either setup: or when: you have to create the environment your controller is acting in.
Populate the request with whatever headers/params/data are appropriate -- headers, parameters, payload, whatever. Setup the grails application configuration with whatever is appropriate if needed.
Your NPE says that you aren't hitting the controller code you are trying to test. The NPE is because your controller doesn't have a converterService assigned/injected (it's a Unit test). You haven't set any params for your controller, so you aren't entering your .each block of code.
Perhaps controller.params.myFile = new MultipartFile() // just guessing here
Abbreviated example from one of my tests (I want to test the json response, so I have to format the request appropriately):
#TestFor(DirectoryController)
#Mock([SpringSecurityService,ConfigurationService,ConfigurationDomain,EntryDomain])
class DirectoryControllerSpec extends Specification
def "test getDirectorySources"() {
setup:
// not testing authentication, so just return true
SpringSecurityUtils.metaClass.static.ifAllGranted = { String role ->
return true
}
// mock the data to be returned from the configurationService call
def configuration = new ConfigurationDomain(id:2,name:'Mock Config',entries:[])
def typeEntry = new EntryDomain(key:'ldap01.type',value:'ad')
configuration.entries << typeEntry
def nameEntry = new EntryDomain(key:'ldap01.name',value:'LDAP01')
configuration.entries << nameEntry
// mock the configurationService
def mockConfigurationService = mockFor(ConfigurationService, true) // loose mock
mockConfigurationService.demand.getConfigurationById() { Long id ->
return configuration
}
controller.configurationService = mockConfigurationService.createMock()
when:
// setup the request attributes
request.setContentType('application/json')
request.method = 'GET'
request.addHeader('Accept','application/json')
controller.params.id = "2"
controller.getDirectorySources()
then:
response.getText() == '{"sources":[{"key":"ldap01","name":"LDAP01","type":"ad"}]}'
cleanup:
// reset the metaClass
SpringSecurityUtils.metaClass = null
}
}
Mock any services used by the controller; you aren't testing your converterService, so that should be mocked, and return a known value so you know what the controller should do in response to the data returned from the service.
In short, in a Unit test, you should control everything not in the immediate controller code.
You should mock your service so your test should look like:
#TestFor(MultipleFileUploadController)
#Mock([ConverterService])
class MultipleFileUploadControllerSpec extends Specification {
...
}
But your test will not pass because you handle NoUploadedFileException in controller, so it will not be caught anywhere in tests, so you should have fail with
Expected exception com.stackoverflow.NoUploadedFileException, but no exception was thrown
Remove the line thrown(NoUploadedFileException) from test.
Try mocking your service above your test class as:
#TestFor(MultipleFileUploadController)
#Mock([ConverterService])
This will solve your issue as spock will mock the ConverterService class for you and you can call its methods. Please keep in mind that each and every domains and services you use in your methods should be mocked in your test. Hope it will help.
I am trying to unit test a service that has a method requiring a request object.
import org.springframework.web.context.request.RequestContextHolder as RCH
class AddressService {
def update (account, params) {
try {
def request = RCH.requestAttributes.request
// retrieve some info from the request object such as the IP ...
// Implement update logic
} catch (all) {
/* do something with the exception */
}
}
}
How do you mock the request object ?
And by the way, I am using Spock to unit test my classes.
Thank you
This code seems to work for a basic unit test (modified from Robert Fletcher's post here):
void createRequestContextHolder() {
MockHttpServletRequest request = new MockHttpServletRequest()
request.characterEncoding = 'UTF-8'
GrailsWebRequest webRequest = new GrailsWebRequest(request, new MockHttpServletResponse(), ServletContextHolder.servletContext)
request.setAttribute(GrailsApplicationAttributes.WEB_REQUEST, webRequest)
RequestContextHolder.setRequestAttributes(webRequest)
}
It can be added as a function to your standard Grails unit test since the function name does not start with "test"... or you can work the code in some other way.
One simple way to mock these, is to modify the meta class for RequestContextHolder to return a mock when getRequestAttributes() is called.
I wrote up a simple spec for doing this, and was quite surprised when it didn't work! So this turned out to be a quite interesting problem. After some investigation, I found that in this particular case, there are a couple of pitfalls to be aware of.
When you retrieve the request object, RCH.requestAttributes.request, you are doing so via an interface RequestAttributes that does not implement the getRequest() method. This is perfectly fine in groovy if the returned object actually has this property, but won't work when mocking the RequestAttributes interface in spock. So you'll need to mock an interface or a class that actually has this method.
My first attempt at solving 1., was to change the mock type to ServletRequestAttributes, which does have a getRequest() method. However, this method is final. When stubbing a mock with values for a final method, the stubbed values are simply ignored. In this case, null was returned.
Both these problems was easily overcome by creating a custom interface for this test, called MockRequestAttributes, and use this interface for the Mock in the spec.
This resulted in the following code:
import org.springframework.web.context.request.RequestContextHolder
// modified for testing
class AddressService {
def localAddress
def contentType
def update() {
def request = RequestContextHolder.requestAttributes.request
localAddress = request.localAddr
contentType = request.contentType
}
}
import org.springframework.web.context.request.RequestAttributes
import javax.servlet.http.HttpServletRequest
interface MockRequestAttributes extends RequestAttributes {
HttpServletRequest getRequest()
}
import org.springframework.web.context.request.RequestContextHolder
import spock.lang.Specification
import javax.servlet.http.HttpServletRequest
class MockRequestSpec extends Specification {
def "let's mock a request"() {
setup:
def requestAttributesMock = Mock(MockRequestAttributes)
def requestMock = Mock(HttpServletRequest)
RequestContextHolder.metaClass.'static'.getRequestAttributes = {->
requestAttributesMock
}
when:
def service = new AddressService()
def result = service.update()
then:
1 * requestAttributesMock.getRequest() >> requestMock
1 * requestMock.localAddr >> '127.0.0.1'
1 * requestMock.contentType >> 'text/plain'
service.localAddress == '127.0.0.1'
service.contentType == 'text/plain'
cleanup:
RequestContextHolder.metaClass = null
}
}
I have the following code in a Java EE managed bean:
FacesContext context = facesContextProvider.getCurrentInstance();
HttpServletResponse response = (HttpServletResponse) context.getExternalContext();
Where facesContextProvider is a custom class for returning the faces context (useful for mock testing).
I'm wondering how to test this in JUnit using mockito. I am trying a combination of:
FacesContextProvider mockFacesContextProvider = mock(FacesContextProvider.class);
when(mockFacesContextProvider.getCurrentInstance()).thenReturn(mockFacesContext);
// this line is wrong ~> when(mockFacesContext.getExternalContext()).thenReturn((ExternalContext) new MockHttpServletResponse());
How can I inject some sort of mock or custom HttpServletResponse into my external context?
Thanks for the help.
ANSWER
My controller code is wrong. You can work with the ExternalContext to do whatever you need. So in the controller, it should actually be:
FacesContext facesContext = facesContextProvider.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
externalContext.responseReset();
If you still wanted the response, you could get it from:
HttpResponse response = externalContext.getResponse();
Then the unit test harness would be:
mockFacesContextProvider = mock(FacesContextProvider.class);
mockFacesContext = mock(FacesContext.class);
mockExternalContext = mock(ExternalContext.class);
mockHttpSession = mock(HttpSession.class);
when(mockFacesContextProvider.getCurrentInstance()).thenReturn(mockFacesContext);
when(mockFacesContext.getExternalContext()).thenReturn(mockExternalContext);
when(mockExternalContext.getSession(true)).thenReturn(mockHttpSession);
And then the Unit Test code would be:
verify(mockExternalContext).responseReset();
FacesContextProvider mockFacesContextProvider = mock(FacesContextProvider.class);
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
when(mockFacesContextProvider.getCurrentInstance()).thenReturn(mockFacesContext);
when(mockFacesContext.getExternalContext()).thenReturn(mockResponse);
I have a grails 2.1 app which has a controller that calls a method on a service, passing in a request and a response:
class FooController {
def myService
def anAction() {
response.setContentType('text/xml')
myservice.service(request,response)
}
I want to unit test this method. And I want to do so using GMock (version 0.8.0), so this is what I tried:
def testAnAction() {
controller.myService = mock() {
service(request,response).returns(true)
}
play {
assertTrue controller.anAction()
}
}
Now this fails saying that that it failed expectations for request.
Missing property expectation for 'request' on 'Mock for MyService'
However, if I write my test like this:
def testAnAction() {
def mockService = mock()
mockService.service(request,response).returns(true)
controller.myService = mockService
play {
assertTrue controller.anAction()
}
}
The test will pass fine. As far as I am aware they are both valid uses of the GMock syntax, so why does the first one fail and the second one not?
Cheers,
I assume you write your tests in a test class FooControllerTest generated by grails.
In a such way, FooControllerTest class is annoted by #TestFor(FooController) wich inject some usefull attributes.
So request is an attribute of your test class, not a variable in the local scope.
It's why it is not reachable from a internal Closure.
I'm convinced that following code could work (I have not tested yet) :
def testAnAction() {
def currentRequest = request
def currentResponse = response
controller.myService = mock() {
service(currentRequest,currentResponse).returns(true)
}
play {
assertTrue controller.anAction()
}
}