Mocking HTTP Interaction with Java, JUnit and MockServer

January 5th, 2016 by

When 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.

Running a Mockserver JUnit Test in Eclipse IDE.

Running a Mockserver JUnit Test in Eclipse IDE.

 

Dependencies

We just need to add two dependencies to our pom.xml:

  • JUnit as test framework and
  • Netty as our web-server implementation.
<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

Search
Categories