Agent47
Agent47

Reputation: 147

Writing a unit test for a method with two HTTP calls with Mockito and jUnit

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

Answers (2)

davidxxx
davidxxx

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 the update() 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

Nir Alfasi
Nir Alfasi

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

Related Questions