25

I'm trying to construct a CloseableHttpResponse mock object to be returned in one of my unit tests, but there's no constructor for it. I found this DefaultHttpResponseFactory, but it only makes a HttpResponse. What's a simple way to construct a CloseableHttpResponse? Do I need to call execute() in my test and then set the statusLine and entity? That seems like a weird approach.

Here's the method I'm trying to mock:

public static CloseableHttpResponse getViaProxy(String url, String ip, int port, String username,
                                                String password) {
    CredentialsProvider credsProvider = new BasicCredentialsProvider();
    credsProvider.setCredentials(
            new AuthScope(ip, port),
            new UsernamePasswordCredentials(username, password));
    CloseableHttpClient httpclient = HttpClients.custom()
            .setDefaultCredentialsProvider(credsProvider).build();
    try {
        RequestConfig config = RequestConfig.custom()
                .setProxy(new HttpHost(ip, port))
                .build();
        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(config);

        LOGGER.info("executing request: " + httpGet.getRequestLine() + " via proxy ip: " + ip + " port: " + port +
                " username: " + username + " password: " + password);

        CloseableHttpResponse response = null;
        try {
            return httpclient.execute(httpGet);
        } catch (Exception e) {
            throw new RuntimeException("Could not GET with " + url + " via proxy ip: " + ip + " port: " + port +
                    " username: " + username + " password: " + password, e);
        } finally {
            try {
                response.close();
            } catch (Exception e) {
                throw new RuntimeException("Could not close response", e);
            }
        }
    } finally {
        try {
            httpclient.close();
        } catch (Exception e) {
            throw new RuntimeException("Could not close httpclient", e);
        }
    }
}

Here's the mock code using PowerMockito:

    mockStatic(HttpUtils.class);
    when(HttpUtils.getViaProxy("http://www.google.com", anyString(), anyInt(), anyString(), anyString()).thenReturn(/*mockedCloseableHttpResponseObject goes here*/)
Popcorn
  • 5,188
  • 12
  • 54
  • 87

9 Answers9

43

Follow these steps may help:

1.mock it (ex. mockito)

CloseableHttpResponse response = mock(CloseableHttpResponse.class);
HttpEntity entity = mock(HttpEntity.class);

2.apply some rules

when(response.getStatusLine()).thenReturn(new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "FINE!"));
when(entity.getContent()).thenReturn(getClass().getClassLoader().getResourceAsStream("result.txt"));
when(response.getEntity()).thenReturn(entity);

3.use it

when(httpClient.execute((HttpGet) any())).thenReturn(response);
nnguyen07
  • 51
  • 2
  • 5
3

It's been a while since this question has been asked, but I want to provide a solution I used.

I've created a small class which extends the BasicHttpResponse class and implements the CloseableHttpResponse interface (which has nothing but a method to close the response). Since the BasicHttpResponse class contains setter methods for pretty much everything, I can set all the fields I need with the following piece of code:

public static CloseableHttpResponse buildMockResponse() throws FileNotFoundException {
    ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
    String reasonPhrase = "OK";
    StatusLine statusline = new BasicStatusLine(protocolVersion, HttpStatus.SC_OK, reasonPhrase);
    MockCloseableHttpResponse mockResponse = new MockCloseableHttpResponse(statusline);
    BasicHttpEntity entity = new BasicHttpEntity();
    URL url = Thread.currentThread().getContextClassLoader().getResource("response.txt");
    InputStream instream = new FileInputStream(new File(url.getPath()));
    entity.setContent(instream);
    mockResponse.setEntity(entity);
    return mockResponse;
}

I basically set all the fields, which are used by the actual code. This also includes reading a mock response content from a file into a stream.

superM
  • 8,605
  • 8
  • 42
  • 51
2

I wanted to create a concrete CloseableHttpResponse rather than a mock, too, so I tracked it down in the Apache HTTP client source code.

In MainClientExec, all the return values from execute look like:

return new HttpResponseProxy(response, connHolder);

where connHolder can be null.

HttpResponseProxy is just a thin wrapper that does a close on the connHolder. Unfortunately, it is package protected, so it's not (necessarily) visible.

What I did was create a "PublicHttpResponseProxy"

package org.apache.http.impl.execchain;

import org.apache.http.HttpResponse;

public class PublicHttpResponseProxy extends HttpResponseProxy {

    public PublicHttpResponseProxy(HttpResponse original) {
        super(original, null);
    }
}

Must be in package "org.apache.http.impl.execchain" (!) Basically bumps the visiblity to public and provide a constructor with null connection handler.

Now I can instantiate a concrete CloseableHttpResponse with

CloseableHttpResponse response = new PublicHttpResponseProxy(basicResponse);

The usual caveats apply. Since the proxy is package protected, it's not a part of the official API, so you might be rolling the dice that it will be unavailable later. On the other hand, there's not much to it, so you could easily just write your own version of it. There would be some cut-and-pasting, but it wouldn't be so bad.

sea-rob
  • 2,275
  • 1
  • 22
  • 22
2

This worked for me:

HttpEntity httpEntity = mock(HttpEntity.class); // mocked

CloseableHttpResponse closeableHttpResponse = mock(CloseableHttpResponse.class) // mocked

CloseableHttpClient closeableHttpClient = mock(CloseableHttpClient .class) // mocked

String resultJson =
    "{\"key\": \"value\"}";

InputStream is = new ByteArrayInputStream( resultJson.getBytes() );

Mockito.when(httpEntity.getContent()).thenReturn(is);
Mockito.when( httpEntity.getContentLength() ).thenReturn(Long.valueOf(.length()));
StatusLine statusLine = new BasicStatusLine(new ProtocolVersion("http", 1, 1), 200, "success");
Mockito.when(httpEntity.toString()).thenReturn(resultJson);
Mockito.when(closeableHttpResponse.getEntity()).thenReturn(httpEntity);
Mockito.when(closeableHttpResponse.getStatusLine()).thenReturn(statusLine);

Mockito.when(closeableHttpClient.execute((HttpPost) 
Mockito.any())).thenReturn(closeableHttpResponse);
Jahnavi Paliwal
  • 1,721
  • 1
  • 12
  • 20
1

Its easy enough to just create a test implementation piggybacking off the existing BasicHttpResponse type:

public class TestCloseableHttpResponse extends BasicHttpResponse implements CloseableHttpResponse {

    public TestCloseableHttpResponse(StatusLine statusline, ReasonPhraseCatalog catalog, Locale locale) {
        super(statusline, catalog, locale);
    }

    public TestCloseableHttpResponse(StatusLine statusline) {
        super(statusline);
    }

    public TestCloseableHttpResponse(ProtocolVersion ver, int code, String reason) {
        super(ver, code, reason);
    }


    @Override
    public void close() throws IOException { }

}
LoganL
  • 11
  • 1
  • 1
    This doesn't seem to compile under httpclient 5. "implements CloseableHttpResponse" <-- gives "Interface expected here" – Alkanshel Feb 11 '21 at 03:06
1

My approach is to create a simple CloseableHttpResponse and then use it as a response when mocking the CloseableHttpClient.

public class TestCloseableHttpResponse extends BasicHttpResponse implements CloseableHttpResponse {

    public TestCloseableHttpResponse(ProtocolVersion ver, int code, String reason) {
        super(ver, code, reason);
    }

        public TestCloseableHttpResponse(ProtocolVersion ver, int code, String reason, String entityString) {
            this(ver, code, reason);
            setEntity(new StringEntity(entityString, Charset.defaultCharset()));
        }

    @Override
    public void close() throws IOException {
        // we don't care about closing any connections in mocking tests
    }
}

and then in tests you can simply write:

CloseableHttpResponse okResponse = new TestCloseableHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");

of if tou want to get some Entity:

CloseableHttpResponse okResponse = new TestCloseableHttpResponse(HttpVersion.HTTP_1_1, 200, "OK", "some text");

In your case you probably want to use mock it like this:

when(httpClient.execute(any())).thenReturn(okResponse);
Benjamin
  • 3,217
  • 2
  • 27
  • 42
0

nvm, I ended up just hacking it by using execute():

private CloseableHttpResponse getMockClosesableHttpResponse(HttpResponse response) throws Exception {
    CloseableHttpClient httpClient = HttpClients.createDefault();
    CloseableHttpResponse closeableHttpResponse = httpClient.execute(new HttpGet("http://www.test.com"));
    closeableHttpResponse.setEntity(response.getEntity());
    closeableHttpResponse.setStatusLine(response.getStatusLine());
    return closeableHttpResponse;
}
Popcorn
  • 5,188
  • 12
  • 54
  • 87
  • 4
    Wouldn't that download a fresh copy of www.test.com each time you call the method? Sounds like a PITA waiting to bite you. – mabi May 22 '15 at 16:02
  • No vote on this one yet, but it is smart and clean, I will use a more reliable SERVER like google.com, which is also light weight to download (assuming it is perfectly legal to do.) instead to build the Object. You can also use an internal SERVER within you organisation if possible. – Motolola Mar 11 '21 at 12:30
0

In my use case, I am using the httpClient in RestTemplate. So I need to mock some additional methods too, like below:

CloseableHttpResponse response = mock(CloseableHttpResponse.class);
HttpEntity entity = mock(HttpEntity.class);
when(response.getStatusLine()).thenReturn(new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"));
when(entity.getContent()).thenReturn(new ByteArrayInputStream("{\"response\": \"test\"}".getBytes()));
when(response.getAllHeaders()).thenReturn(new BasicHeader[]{new BasicHeader("test", "test")});
when(response.getEntity()).thenReturn(entity);
doNothing().when(response).close();

when(httpClient.execute(any(), (HttpRequest) any(), (HttpContext) any())).thenReturn(response);
Antony Vimal Raj
  • 364
  • 3
  • 14
0

Use mockito inline that is mentioned here, it lets you to mock final classes