Mocking HTTP Interaction with Java, JUnit and MockServer
January 5th, 2016 by Micha KopsWhen writing tests for our software components sometimes we need to mock external services based on the HTTP protocol, might it be a RESTful web-service, an XML-RPC call or a simple GET request to some web-server.
In the following short tutorial I’d like to demonstrate how to create a mock HTTP server for testing and how to bootstrap and bind it to the life-cycle of a classical build-management tool like Maven.
Contents
Dependencies
We just need to add two dependencies to our pom.xml:
<dependency> <groupId>org.mock-server</groupId> <artifactId>mockserver-netty</artifactId> <version>3.10.2</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
JUnit Rule
Starting our mock HTTP server is quite easy as we’re just using a JUnit role here to bootstrap the server.
In the following example, we’re bootstrapping an instance to run on port 9000 and to return a HTTP status code of 200 when the URL part “/foo” is called.
Afterwards we’re using a standard JAX-RS client to send a request to the HTTP server and we’re verifying that the response status is 200.
In the last step, the mock-server-client allows us to verify that the mock-server has received exactly one request for “/foo”.
package it; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.core.Response; import org.junit.Rule; import org.junit.Test; import org.mockserver.client.server.MockServerClient; import org.mockserver.junit.MockServerRule; import org.mockserver.model.HttpRequest; import org.mockserver.model.HttpResponse; import org.mockserver.verify.VerificationTimes; public class HttpTest { @Rule public MockServerRule mockServerRule = new MockServerRule(this, 9000); private MockServerClient mockServerClient; @Test public void shouldConnectToHttpService() throws Exception { // setting behaviour for test case mockServerClient.when(HttpRequest.request("/foo")).respond(HttpResponse.response().withStatusCode(200)); // create a GET request using JAX-RS rest client API Client client = ClientBuilder.newClient(); Response response = client.target("http://localhost:9000").path("/foo").request().get(); // assert response assertThat(response.getStatus(), equalTo(200)); // verify server has received exactly one request mockServerClient.verify(HttpRequest.request("/foo"), VerificationTimes.once()); } }
We may now run our tests in our IDE using the JUnit runner or using Maven like this:
$ mvn test ------------------------------------------------------- T E S T S ------------------------------------------------------- Running it.HttpTest [..] 17:17:15.383 [MockServer thread for port: 9000] DEBUG i.n.util.internal.ThreadLocalRandom - -Dio.netty.initialSeedUniquifier: 0x373b4db8098b0b21 (took 8 ms) 17:17:15.531 [main] DEBUG o.m.client.netty.NettyHttpClient - Sending request: { "method" : "PUT", "path" : "/expectation", "body" : { "charset" : "UTF-8", "type" : "STRING", "string" : "{\n \"httpRequest\" : {\n \"path\" : \"/foo\"\n },\n \"httpResponse\" : {\n \"statusCode\" : 200\n },\n \"times\" : {\n \"remainingTimes\" : 0,\n \"unlimited\" : true\n },\n \"timeToLive\" : {\n \"unlimited\" : true\n }\n}" } } [..] 17:17:15.755 [nioEventLoopGroup-3-1] INFO o.m.mockserver.MockServerHandler - creating expectation: { "httpRequest" : { "path" : "/foo" }, "times" : { "remainingTimes" : 0, "unlimited" : true }, "timeToLive" : { "unlimited" : true }, "httpResponse" : { "statusCode" : 200 } } [..] 17:17:16.018 [nioEventLoopGroup-3-2] INFO o.m.matchers.HttpRequestMatcher - request: { "method" : "GET", "path" : "/foo", "headers" : [ { "name" : "User-Agent", "values" : [ "Jersey/2.5 (HttpUrlConnection 1.8.0_45)" ] }, { "name" : "Host", "values" : [ "localhost:9000" ] }, { "name" : "Accept", "values" : [ "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2" ] }, { "name" : "Connection", "values" : [ "keep-alive" ] }, { "name" : "Content-Length", "values" : [ "0" ] } ], "keepAlive" : true, "secure" : false } [..] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.067 sec [..] Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
Maven Lifecycle Integration
The Maven plug-in allows us boot mock server instances when a specific Maven life-cycle phase is reached as in the following example (I have used a Maven profile named “start-mockserver” here to explicitly start the mock-server when this Maven profile is used..):
<profile> <id>start-mockserver</id> <activation> <activeByDefault>false</activeByDefault> </activation> <build> <plugins> <plugin> <groupId>org.mock-server</groupId> <artifactId>mockserver-maven-plugin</artifactId> <version>3.10.1</version> <configuration> <serverPort>9000</serverPort> <proxyPort>9000</proxyPort> <logLevel>DEBUG</logLevel> </configuration> <executions> <execution> <id>process-test-classes</id> <phase>process-test-classes</phase> <goals> <goal>start</goal> </goals> </execution> <execution> <id>verify</id> <phase>verify</phase> <goals> <goal>stop</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile>
To boot the mock server, we now just need to run our tests with this specific profile:
mvn -Pstart-mockserver test
Tutorial Sources
Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:
git clone https://bitbucket.org/hascode/mockserver-junit-tutorial.git
Resources
Other Testing Tutorials of mine
- Testing Asynchronous Applications with Java and Awaitility
- Mutation Testing with Pitest and Maven
- Running categorized Tests using JUnit, Maven and Annotated-Test Suites
- Ordering your JUnit Rules using a RuleChain
- Mocking, Stubbing and Test Spying using the Mockito Framework and PowerMock
- Make your Tests more readable with custom Hamcrest Matchers
- Selenium WebDriver, Selenium Server and PageObjects by Example
- Performance Testing a Multiuser Web Application with JMeter and Maven
- Marrying Java EE and BDD with Cucumber, Arquillian and Cukespace
- BDD Testing with Cucumber, Java and JUnit
- Java EE: Setting up and Testing Form-Based JDBC Authentication with Arquillian and Maven
- Running JavaScript Tests with Maven, Jasmine and PhantomJS
- Writing Java Integration Tests for MongoDB
- Arquillian Transaction Extension: Transaction Rollback for your Java EE Integration Tests
- A short Introduction to ScalaTest
- and more ..
Tags: fake, http, https, integrationtest, junit, maven, mock, mockserver, rest, server, stub, test, verify
May 5th, 2016 at 2:24 pm
Thanks for the complete tutorial!!
May 5th, 2016 at 4:01 pm
Hi Emilie,
thanks, you’re welcome! :)
January 11th, 2017 at 6:38 am
Hi,
I am facing some issues while writing the test cases for JAX-RS Rest Client.
In Rest there will be 2 ends one is Rest Resource(Rest Service) and Rest Client who will consume the Rest Resource. I have developed the code for Rest Client which is working fine. But I don’t understand how to write the JUnit Test cases for this client code. because the output from the rest resource is the hashcode of object which will be unique and for every time when my client will make the HTTP call it will be different so so how I will write the test cases for my rest client.
and for this do I need to create a different rest resource or the same rest resource will be used for testing the rest client code and one more thing suppose if I will create one different rest resource for testing the rest client then how this rest resource will be up and running for each time when i will test my rest client or do I need to keep this rest resource up and running.
Please give me some idea around it as soon as possible.