Reputation: 147
I have three methods,
Here are the implementations(not the full implementation),
String getId(String accessToken, Pojo p) {
//Note that the pojo is for formatting the payload for the request, nothing more
HttpResponse response = httpRequestService.makeGetRequest(url, TOKEN_PREFIX, accessToken,
CONTENT_TYPE);
if (response.getStatusLine().getStatusCode() == 200) {
log.debug("Success");
}
//Code for getting the id from the response
return id;
}
void update(String accassToken, String id, StringEntity payload) {
HttpResponse response = httpRequestService.makePutRequest(url + id,
TOKEN_PREFIX, accessToken, CONTENT_TYPE, payload);
if (response.getStatusLine().getStatusCode() == 200) {
log.debug("Success");
}
}
void save(String accessToken, String payload) {
//The getId() is called here
String id = getId(/*arguments*/);
if(id == null) {
log.error("Error");
} else {
//The update() is called here
update(/*arguments*/);
}
}
As mentioned, the getId and update methods are called inside save method and both getId and update methods have HTTP calls.
I have to write a unit test for the save() method. This is what I tried.
//This is an interface with methods to call HTTP requests.
HttpRequestService httpRequestService = Mockito.mock(HttpRequestService.class);
//Constructor injection
ClassWithMethods a = new ClasswithMethods(httpRequestService);
HttpResponse responseGet = Mockito.mock(HttpResponse.class);
StatusLine statusLineGet = Mockito.mock(StatusLine.class);
HttpEntity httpEntity = Mockito.mock(HttpEntity.class);
Mockito.when(responseGet.getStatusLine()).thenReturn(statusLineGet);
Mockito.when(statusLineGet.getStatusCode()).thenReturn(200);
Mockito.when(responseGet.getEntity()).thenReturn(httpEntity);
Mockito.when(httpEntity.getContent()).thenReturn(IOUtils.toInputStream(stream));
Mockito.when(httpRequestService.makeGetRequest(url, TOKEN_PREFIX, accessToken,
CONTENT_TYPE).thenReturn(responseGet);
HttpResponse responsePut = Mockito.mock(HttpResponse.class);
StatusLine statusLinePut = Mockito.mock(StatusLine.class);
Mockito.when(responsePut.getStatusLine()).thenReturn(statusLinePut);
Mockito.when(statusLinePut.getStatusCode()).thenReturn(200);
Mockito.when(httpRequestService.makePutRequest(url + id, TOKEN_PREFIX,
accessToken, CONTENT_TYPE, payloadEntity).thenReturn(responsePut);
a.save(accessToken, payload);
But when testing, the responsePut returns a null. The arguments are also matching.
Problem : How to test this save method which calls two HTTP methods?
This may not be the best way to do this. If there better ways to test a method like this, please suggest.
Thank you
Note that, httpRequestService is an interface with methods for HTTP calls and the constructor injection is used too.
Upvotes: 5
Views: 16387
Reputation: 131496
About your way of testing
Actually, your test setup is fine but complicated to read.
A test has to keep readable and simple.
Otherwise understanding and maintaining it becomes hard.
You want to mock HttpRequestService
.
It is a good way if you want to unit-test
your class and don't want consider integration concerns.
But the way you mock dependencies of the mocked HttpRequestService
makes the code not readable :
HttpResponse responsePut = Mockito.mock(HttpResponse.class);
StatusLine statusLinePut = Mockito.mock(StatusLine.class);
Mockito.when(responsePut.getStatusLine()).thenReturn(statusLinePut);
Mockito.when(statusLinePut.getStatusCode()).thenReturn(200);
And you repeat this at each invocation.
Extract them in a method :
public HttpResponse createMockedHttpResponse(String url){
HttpResponse responsePut = Mockito.mock(HttpResponse.class);
StatusLine statusLinePut = Mockito.mock(StatusLine.class);
Mockito.when(responsePut.getStatusLine()).thenReturn(statusLinePut);
Mockito.when(statusLinePut.getStatusCode()).thenReturn(200);
return responsePut;
}
About the test failure
It just throws a
NullPointerException
at the line,
if (response.getStatusLine().getStatusCode() == 200)
in theupdate()
method.When I debugged it, it shows the responsePut is null.
If I look your actual code, these invocations in the class under test :
getStatusLine().getStatusCode()
are correctly mocked here :
HttpResponse responsePut = Mockito.mock(HttpResponse.class);
StatusLine statusLinePut = Mockito.mock(StatusLine.class);
Mockito.when(responsePut.getStatusLine()).thenReturn(statusLinePut);
Mockito.when(statusLinePut.getStatusCode()).thenReturn(200);
By elimination, it means that the recorded behavior at the next statement was not encountered:
Mockito.when(httpRequestService.makePutRequest(url + id, TOKEN_PREFIX,
accessToken, CONTENT_TYPE, payloadEntity).thenReturn(responsePut);
So ensure you that the record behavior for makePutRequest()
and the actual invocation of makePutRequest()
have exactly same / equals()
passed parameters.
If some parameters are not really required for the test logic, don't hesitate to pass Mockito.any()
Upvotes: 1
Reputation: 53545
You should extract the following into a separate method (same goes for PUT):
HttpResponse response = httpRequestService.makeGetRequest(url, TOKEN_PREFIX, accessToken,
CONTENT_TYPE);
Then you can stub that method using mockito's:
when(obj.methodGET(...)).thenReturns(...)
Remember: your unit-tests should not make calls over-the-wire!
EDIT
private HttpResponse get(String url, ...) {
return httpRequestService.makeGetRequest(url, TOKEN_PREFIX, accessToken,
CONTENT_TYPE);
}
private HttpResponse put(String url, ...) {
return httpRequestService.makePutRequest(url + id,
TOKEN_PREFIX, accessToken, CONTENT_TYPE, payload);
}
And then:
...
when(mockedStatusLineObj.getStatusCode()).thenReturns(200);
HttpResponse mockedGet = mock(HttpResponse.class);
when(mockedGet.getStatusLine()).thenReturns(mockedStatusLineObj);
when(object.get(...)).thenReturns(mockedGet);
when(object.put(...)).thenReturns(...);
Upvotes: 3