Getting started with: WireMock

This is the seventh article in our series on new, popular or otherwise interesting tools used in test automation. You can read all posts within this series by clicking here.

What is WireMock?
From the WireMock.org website: WireMock is a flexible library for stubbing and mocking web services. Unlike general purpose mocking tools it works by creating an actual HTTP server that your code under test can connect to as it would a real web service. It supports HTTP response stubbing, request verification, proxy/intercept, record/playback of stubs and fault injection, and can be used from within a unit test or deployed into a test environment. Although it’s written in Java, there’s also a JSON API so you can use it with pretty much any language out there.

In short, WireMock is a very handy tool for all those situations where you need to set up a mock version of a web service, for example for testing or development purposes.

Where can I get WireMock?
WireMock can be downloaded from the WireMock Maven repository.

How do I install and configure WireMock?
Installing WireMock is as simple as downloading the latest standalone version from the WireMock Maven repository and adding it as a dependency to your Java project.

Creating a first WireMock mock service
Let’s start by creating a very simple mock service that returns a plain text string and a HTTP status code.

public void setupStub() {
		
	stubFor(get(urlEqualTo("/an/endpoint"))
            .willReturn(aResponse()
                .withHeader("Content-Type", "text/plain")
                .withStatus(200)
                .withBody("You've reached a valid WireMock endpoint")));
}

This code snippet creates a simple mock service that runs at the /an/endpoint endpoint on the WireMock server (your localhost most of the time, but more about that in a bit). It returns a response with content type text/plain, HTTP status 200 and body text “You’ve reached a valid WireMock endpoint“.

Running and testing your mock service
Of course, we would also like to validate whether the mock service we created does what we told it to do. In this post, I will use JUnit-based REST Assured tests to validate the behaviour of our mocks. But first, we need to get our mock service up and running before we start testing. This can be done very easily with WireMock using a JUnit @Rule:

@Rule
public WireMockRule wireMockRule = new WireMockRule(8090);

This rule starts and stops the mocks defined in your JUnit test class for every test in it. Next, we can define a couple of REST Assured tests to see whether the mock service is doing what we expect:

@Test
public void testStatusCodePositive() {
		
	setupStub();
		
	given().
	when().
		get("http://localhost:8090/an/endpoint").
	then().
		assertThat().statusCode(200);
}
	
@Test
public void testStatusCodeNegative() {
	
	setupStub();
		
	given().
	when().
		get("http://localhost:8090/another/endpoint").
	then().
		assertThat().statusCode(404);
}
	
@Test
public void testResponseContents() {
		
	setupStub();
	
	String response = get("http://localhost:8090/an/endpoint").asString();
	Assert.assertEquals("You've reached a valid WireMock endpoint", response);
}

When we run these tests, they will all pass, indicating our mock service behaves as expected:
WireMock JUnit test results

Request-response matching
WireMock also allows you to define a stub that returns a particular answer based on (part of) the request message using the matching matcher method:

public void setupStub() {
		
	stubFor(post(urlEqualTo("/pingpong"))
			.withRequestBody(matching("<input>PING</input>"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/xml")
                .withBody("<output>PONG</output>")));
}

This service only returns a status code 200 and a response XML message when the request body equals a given value. Again, we can validate this easily using REST Assured:

@Test
public void testPingPongPositive() {
		
	setupStub();
		
	given().
		body("<input>PING</input>").
	when().
		post("http://localhost:8090/pingpong").
	then().
		assertThat().
		statusCode(200).
		and().
		assertThat().body("output", org.hamcrest.Matchers.equalTo("PONG"));
}

WireMock also allows you to use regular expressions for more flexible request matching.

Creating stateful mocks
As a final example, I’ll show how to create a stateful mock. This is done in WireMock using scenarios:

public void setupStub() {
		
	stubFor(get(urlEqualTo("/todolist"))
			.inScenario("addItem")
			.whenScenarioStateIs(Scenario.STARTED)
			.willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/xml")
                .withBody("<list>Empty</list>")));
		
	stubFor(post(urlEqualTo("/todolist"))
			.inScenario("addItem")
			.whenScenarioStateIs(Scenario.STARTED)
			.willSetStateTo("itemAdded")
			.willReturn(aResponse()
				.withHeader("Content-Type", "application/xml")
                .withStatus(201)));
		
	stubFor(get(urlEqualTo("/todolist"))
			.inScenario("addItem")
			.whenScenarioStateIs("itemAdded")
			.willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/xml")
                .withBody("<list><item>Item added to list</item></list>")));	
}

So, when we first perform a GET on this mock, an empty to-do list is returned. Then, when we do a POST, the mock service state is changed from Scenario.STARTED (a default in WireMock) to “itemAdded“. Then, when we do another GET, we get a to-do list with a single item on it:

@Test
public void testStatefulMock() {
		
	setupStub();
		
	given().
	when().
		get("http://localhost:8090/todolist").
	then().
		assertThat().
		statusCode(200).
		and().
		assertThat().body("list", org.hamcrest.Matchers.equalTo("Empty"));
		
	given().
	when().
		post("http://localhost:8090/todolist").
	then().
		assertThat().
		statusCode(201);
		
	given().
	when().
		get("http://localhost:8090/todolist").
	then().
		assertThat().
		statusCode(200).
		and().
		assertThat().body("list", org.hamcrest.Matchers.not("Empty")).
		and().
		assertThat().body("list.item", org.hamcrest.Matchers.equalTo("Item added to list"));
}

A trivial example, perhaps, but nevertheless it shows you how can create stateful mocks easily using WireMock.

Further reading
WireMock provides many more useful features for creating just the mock you want. A complete reference guide can be found on the WireMock website.

An Eclipse project including the mock services and REST Assured tests I’ve used in this post can be downloaded here.

Testing web services using in-memory web containers

Last week, I read an interesting blog post on how to test Java web services inside an in-memory web container. I thought it was a fast and elegant solution to overcome a well-known unit testing problem: unit tests for web services become cumbersome when your web services uses the services provided by the container and you need to mock these container services. On the other hand, if you don’t mock these, your unit tests are virtually worthless. The blog post, originally written by Antonio Goncalves, provides an elegant solution to this problem: use an in-memory container. In this case, he uses Sun’s implementation of Java SE 6, which includes a light-weight HTTP server API and implementation: com.sun.net.httpserver.

In this post, I will show a slightly improved version of his solution using my own web service implementation. In order to get the example below to work, all I needed to do was include a recent version of the javax.jws library, which you can find for example here.

I have constructed a simple service that, given a composer, returns his name, his best known work, his country of birth and his age of death. The interface for this service looks like this:
Continue reading

Testing REST services with REST Assured

For those of you wanting to add the possibility to validate RESTful web services to your test automation framework, REST Assured can be a very useful way to do just that. In this post, I want to show you how to perform some basic tests on both JSON and XML-based REST services.

Installing and configuring REST Assured is easy, just download the latest version from the website and add the relevant .jar files to your testing project and you’re good to go.

Testing a REST service that returns XML
First, let’s try validating a REST service that returns a response in XML format. As an example, I use the ParaBank REST service to get the customer details for a customer with ID 12212 (click here to invoke the service and see the response in your browser). For this response, I want to check that the returned customer ID is equal to 12212, that the first name is equal to John, and that the last name is equal to Doe. The following REST Assured-test does exactly that. Note that the test consists of just a single statement, but I’ve added some line breaks to make it more readable.

package com.ontestautomation.restassured.test;

import static com.jayway.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class RestServiceTest {
	
	public static void main (String args[]) {
		
		get("http://parabank.parasoft.com/parabank/services/bank/customers/12212/").
		then().
			assertThat().body("customer.id", equalTo("12212")).
		and().
			assertThat().body("customer.firstName", equalTo("John")).
		and().
			assertThat().body("customer.lastName", equalTo("Doe"));		
	}
}

Pretty straightforward, right? All you need to do is invoke the service using get() and then perform the required checks using assertThat() and the equalTo() matcher (which is part of Hamcrest by the way, not of REST Assured). Identifying the response elements to be checked is done using a dot notation. For example, customer.id identifies the id element that is a child of the customer element, which is the root element of the response message.

When we run this test, we see the results displayed neatly in our IDE:
REST Assured test resultsOne of the assertions fails, since the actual last name for customer 12212 is Smith, not Doe.

Testing a REST service that returns JSON
REST Assured can be used just as easily to perform checks on REST services that return JSON instead of XML. In the next example, I use a REST service that takes a text string and returns the md5 checksum for that string, together with the original string.
REST service with JSON responseThe following code example checks both the original string value and the md5 checksum value for this REST service:

package com.ontestautomation.restassured.test;

import static com.jayway.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class RestServiceTestGherkin {
	
	public static void main(String args[]) {
		
		given().
			parameters("text", "test").
		when().
			get("http://md5.jsontest.com").
		then().
			body("md5",equalTo("098f6bcd4621d373cade4e832627b4f6")).
		and().
			body("original", equalTo("incorrect"));
	}
}

As you can see, checking values from JSON responses is just as straightforward as is the case for XML responses. Two additional things do have changed in this example though:

  • As this web service call takes parameters – in this case a single parameter containing the string for which the md5 value is to be calculated – we need to define the value of this parameter before calling the service. This is done using the parameters() method, which takes parameter key-value pairs as its argument. Alternatively, we could have omitted the parameters call and just perform a get(“http://md5.jsontest.com?text=test”) instead, but this is both more elegant and more maintainable.
  • Also, as you can see from this example, in REST Assured, you can specify your tests in a BDD format using the Given-When-Then construction that is also used (for example) in Cucumber.

When we run this test, again, the test results show up nicely in our IDE:
Test results for JSON REST service testI’ve added another defect on purpose to show how they are displayed: the value of the original element in the response should of course be test instead of incorrect.

Combining REST Assured and TestNG
Finally, we can also combine REST Assured with TestNG to create an even more powerful REST service testing framework. In that way, you can for example use the reporting capabilities of TestNG for your REST service tests, as well as integrate REST Assured tests into an existing test framework. Doing so is as easy as replacing the default Hamcrest assertions with TestNG assertions and performing the required checks:

package com.ontestautomation.restassured.test;

import static com.jayway.restassured.RestAssured.*;

import org.testng.Assert;
import org.testng.annotations.Test;

import com.jayway.restassured.path.xml.XmlPath;

public class RestServiceTestNG {
	
	@Test
	public void testng() {
		
		// Get the response XML from the REST service and store it as a String
		String xml = get("http://parabank.parasoft.com/parabank/services/bank/customers/12212/").andReturn().asString();
		
		// Retrieve the values to be checked from the XML as a String
		XmlPath xmlPath = new XmlPath(xml).setRoot("customer");
		String customerId = xmlPath.getString("id");
		String firstName = xmlPath.getString("firstName");
		String lastName = xmlPath.getString("lastName");
		
		// Perform the required checks
		Assert.assertEquals(customerId, "12212");
		Assert.assertEquals(firstName, "John");
		Assert.assertEquals(lastName, "Doe");
	}
}

When using TestNG, we first store the response of the web service as a String, after which we can check whatever we want to check. It does make the test code a little longer, and in my personal opinion a little less elegant. On the upside, we can now seamlessly integrate our REST Assured-tests in a larger TestNG-based test framework and profit from the automatic report generation that TestNG provides.

Running this test results in the following output in our IDE:
Test results for TestNG-based testsand the following HTML report:
HTML results for the TestNG-based REST Assured testsAdditional features
Apart from the options shown above, REST Assured provides some other features that can come in very handy when testing REST-based web services. For instance, you can also test services that require authentication in order to be invoked. For basic authentication, you can simply add the credentials to your test statement as follows:

given().auth().basic("username", "password").when().

It is also possible to invoke services that require OAuth authorization (this requires the help of the Scribe library):

given().auth().oauth(..).when().

If you want to explicitly validate that a web service returns the correct content type (JSON, in this case), you can do this:

get("/path/to/service").then().assertThat().contentType(ContentType.JSON)

For a complete overview of all REST Assured features, you can refer to the online documentation.

Happy REST testing!