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.
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 GitHub repository, fork it there or clone it using Git:
git clone https://bitbucket.org/hascode/mockserver-junit-tutorial.git
Other Testing Tutorials of mine
-
Running categorized Tests using JUnit, Maven and Annotated-Test Suites
-
Mocking, Stubbing and Test Spying using the Mockito Framework and PowerMock
-
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
-
Java EE: Setting up and Testing Form-Based JDBC Authentication with Arquillian and Maven
-
Arquillian Transaction Extension: Transaction Rollback for your Java EE Integration Tests