Core Java

Unit testing HTTP calls with LocalTestServer

There are times when you’re unit testing code that is making HTTP calls to a remote server. You could be using a library such as Apache’sHttpClient or Spring’s RestTemplate to do so.

Of course, you don’t want to rely on a remote service for your unit tests. Besides the overhead involved (remember that unit test are supposed to be fast) you simply cannot rely on remote services being available during execution of your tests. You probably are also not able to completely control the response for all of your test scenarios.

Consider the following simplified example.

ExampleHttpCall

public class ExampleHttpCall {

    private String serviceUrl;

    public ExampleHttpCall(String url) {
        serviceUrl = url;
    }

    public String doGet() {
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(serviceUrl, String.class);
        String response = responseEntity.getBody();
        return response;
    }

}

How would you go about writing a unit test for the ExampleHttpCall?

You could of course redesign the class in such a manner that an instance of the RestTemplate gets injected into the class:

ExampleHttpCall alternate version

@Component
public class ExampleHttpCallAlternateVersion {

    @Resource
    private RestTemplate restTemplate;

    private String serviceUrl;

    public ExampleHttpCallAlternateVersion(String url) {
        serviceUrl = url;
    }

    public String doGet() {
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(serviceUrl, String.class);
        String response = responseEntity.getBody();
        return response;
    }

}

The dependency can now be mocked giving you great control. However, this approach also incurs increased complexity due to additional configuration. Furthermore, you could end up with a lot of tedious mocking.

For this simple example using a mock probably is the way to go. But this may not always be the case. If so, another possible approach employs the use of a local test server. As it happens, the Apache HttpClient project provides a LocalTestServer in its tests artifact. If you’re using Maven you can include it by adding the following dependency:

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.3.6</version>
	<classifier>tests</classifier>
	<scope>test</scope>
</dependency>

Now you can set up the server in your unit test:

LocalTestServer set up

private LocalTestServer server = new LocalTestServer(null, null);

@Before
public void setUp() throws Exception {
    server.start();
}

@After
public void tearDown() throws Exception {
    server.stop();
}

Only starting and stopping a server doesn’t get you very far, of course. So there is one more ingredient that you’ll be needing. You’ll be wanting to register one or more handlers that implement the interface org.apache.http.protocol.HttpRequestHandler, e.g.:

register your handler

server.register("/foo/*", myHttpRequestHandler);

The HttpRequestHanlder interface will have you implement the method void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException;

This will method will give you full control over the HTTP response.

So for our original example a minimal unit test could look something like the following code:

Basic unit test

public class ExampleHttpCallTest {


    private ExampleHttpCall exampleHttpCall;

    private LocalTestServer server = new LocalTestServer(null, null);

    private HttpRequestHandler myHttpRequestHandler = new HttpRequestHandler() {

        @Override
        public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
            response.setEntity(new StringEntity("foobar"));
        }

    };

    @Before
    public void setUp() throws Exception {
        server.start();
        server.register("/foo/*", myHttpRequestHandler);

        String serverUrl = "http:/" + server.getServiceAddress();
        exampleHttpCall = new ExampleHttpCall(serverUrl +"/foo/bar");
    }

    @After
    public void tearDown() throws Exception {
        server.stop();
    }

    @Test
    public void test() {
        String result = exampleHttpCall.doGet();
        assertEquals("foobar", result);
    }

}

That’s all that it takes to get started. From here on you can elaborate by adding test cases for every possible scenario.

Reference: Unit testing HTTP calls with LocalTestServer from our JCG partner Wim van Haaren at the JDev blog.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Achmad Nasirudin Sandi

Just sharing what I tried at https://github.com/achmadns/project-seed/blob/local-test-server/src/test/java/LocalTest.java

Thank you Wim to let us know this useful feature.

Back to top button