In software development, testing is essential. And because tests are so important, we have today more and more tools to help us write efficient tests. In this article, we’ll discuss Wiremock, a tool you can use to simulate other services on which your app depends during a unit/integration test execution. We also name this way of simulating a dependency service “stubbing“.
Take a look at the next scenario (figure 1). We have an app that accepts payments from its users. To execute a payment, the app calls an external service named a Payment Service Provider. When you test the payment use case logic of your app, you, however, don’t want to call also the external service. You need to “fake” it somehow.
To fake a dependency in a test, we have two options:
In the next sections, we discuss stubs vs. mocks and then we use WireMock in a practical demonstration of writing a test.
A mock is a fake object you control to influence a test scenario. For example, in our case, suppose your app declares a proxy object to connect to the Payment Service Provider (external) service. You may mock this object (figure 2) to eliminate the dependency on the external service, and, by controlling this object, you define specific test scenarios (for example, what happens when the connection to the external service fails and what happens when the connection it’s successful).
An alternative to using a mock is faking the service that is your app’s dependency. In our scenario, instead of mocking the app’s proxy object that connects to the external service, you create a fake service (figure 3). This fake service is what we call a stub. Similarly to using a mock, you control a stub to define various test scenarios.
In this section, we implement a small app that calls a dependency via a REST endpoint. Then, we use WireMock to write a test scenario for this demo application. You find here the app used for the demonstration of this article: https://github.com/lspil/blog/tree/master/wiremock-for-testing
For this example, I will use HttpBin.org as the dependency I want to stub. I like using HttpBin.org when implementing demonstrations where I simply need to have an endpoint to call. I use HttpBin.org and don’t have to implement one more service. For this example, we’ll call the /anything endpoint httpbin.org exposes. Our app exposes a /demo endpoint, and when this endpoint is called, the app calls further the httpbin.org/anything endpoint.
Let’s start with building the app. You can take the app from here: You find here the app used for the demonstration of this article: https://github.com/lspil/blog/tree/master/wiremock-for-testing
Let’s start a Spring Boot project and add the needed dependencies in the pom.xml file (yes, I use Maven 🙂 ). The next code snippet shows you the dependencies I added in the pom.xml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock</artifactId> <version>2.26.3</version> <scope>test</scope> </dependency> |
Next, create an interface to define the OpenFeign client as presented in the next code snippet. Don’t forget to give the Feign client a name and specify the base URL.
1 2 3 4 5 6 | @FeignClient(name = "httpbin", url = "${base.url.httpbin}") public interface HttpBinProxy { @GetMapping("/anything") String getAnything(); } |
Observe that we take the base URL value from the application properties file. In any real-world app, it’s mandatory to avoid hardcoding details that are environment-specific, such as URLs. In our case, we also need this in the example, because, when we make a stub with WireMock for HttpBin, the stub will have a local address. We’ll solve this problem very easily with a Spring Boot profile as you’ll see further in this article.
Add the base.url.httpbin property in the application.properties file as presented in the next code snippet. From the app, you refer to this property using the standard SpEL expression ${}
1 | base.url.httpbin=http://httpbin.org |
When using OpenFeign, you need to explicitly enable this capability in a configuration class using the @EnableFeignClients annotation as presented in the next code snippet. With the @EnableFeignClients annotation, you also tell the app in which packages to seek for the OpenFeign client interfaces.
1 2 3 4 | @Configuration @EnableFeignClients(basePackages = "com.example.wiremockfortesting.proxy") public class ProjectConfig { } |
In a controller class, we expose the /demo endpoint. When calling the /demo endpoint, the app uses the proxy interface to call the httpbin.org service.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @RestController public class DemoController { private final HttpBinProxy httpBinProxy; public DemoController(HttpBinProxy httpBinProxy) { this.httpBinProxy = httpBinProxy; } @GetMapping("/demo") public String getAnything() { return httpBinProxy.getAnything(); } } |
This is the app we use for the demonstration. You can manually test it before implementing an integration test with WireMock. Start the application and call the /demo endpoint using Postman, cURL or another similar tool.
1 | curl http://localhost:8080/demo |
In this section, we write a test for the /demo endpoint. We use WireMock to define a stub the app calls instead of the real service (HttpBin.org). We can control the stub’s behavior to define various test scenarios.
Let’s start by defining a test profile for the application. The test profile helps us defining custom properties we use when running the unit and integrations tests. For example, in this case, we need to run the stub instead of the real dependency (HttpBin.org) and we’ll configure the stub to run locally on port 7070. Of course, you can choose a different port if you wish.
We add to the resources folder of our Maven project, a file named application-test.properties. The file’s contents are presented in the next code snippet.
1 | base.url.httpbin=http://localhost:7070 |
We then make sure the app’s tests use this new profile by annotating the test class with the @ActiveProfiles annotation as presented in the next code snippet. Setting the “test” profile as the active profile, we know the tests will take the configuration from the application-test.properties. This means that the app won’t try to call anymore HttpBin.org, but localhost:7070. We will configure our fake service (the stub) to run on localhost:7070 and this way we know the test uses it.
1 2 3 4 5 6 | @SpringBootTest @ActiveProfiles("test") class WiremockForTestingApplicationTests { // Omitted code } |
We also add the @AutoConfigureMockMvc because we’ll use MockMvc to call the /demo endpoint for the test. We could have as well call directly the service method depending on what kind of test we want to implement. For this article, I chose to use as an example an integration test by calling the REST endpoints directly with MockMvc.
1 2 3 4 5 6 7 8 9 10 11 12 13 | @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("test") class WiremockForTestingApplicationTests { @Autowired private MockMvc mockMvc; // Omitted code } |
To configure the WireMock stub, we use the WireMockServer class as presented in the next code snippet. We start the stub on localhost:7070 which we know the app will call as this is what we defined in the application-test.properties file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("test") class WiremockForTestingApplicationTests { private static WireMockServer wireMockServer; @Autowired private MockMvc mockMvc; @BeforeAll static void init() { wireMockServer = new WireMockServer( new WireMockConfiguration().port(7070) ); wireMockServer.start(); WireMock.configureFor("localhost", 7070); } // Omitted code } |
You can now write the test. In the next snippet, I omitted the declaration of MockMvc and the WireMock server which you find in the previous listings of the article so you can focus only on the test.
We begin by using the stubFor() method to configure the WireMock stub. It’s like telling WireMock:
“You should expect the app will call the /anything endpoint using the HTTP GET method. When that happens, you should reply with an HTTP status of 200 OK”.
You can decide how you want the stub to behave. If you wanted to return an HTTP 404 you can do that as well. Or maybe you wanted the stub to return specific headers or a body in the HTTP response. You can configure the WireMock stub to act as you wish for the test scenario you want to implement.
You then call the endpoint using MockMvc and define what is the expected behavior according to the assumptions.
In the end, you may use the WireMock.verify() method to validate that the app correctly interacted with the stub.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | @SpringBootTest @AutoConfigureMockMvc @ActiveProfiles("test") class WiremockForTestingApplicationTests { // Omitted code @Test void testCallHttpBinAnything() throws Exception { stubFor(WireMock.get(urlMatching("/anything")) .willReturn(aResponse() .withStatus(OK.value()))); mockMvc.perform(get("/demo")) .andExpect(status().isOk()); verify(getRequestedFor(urlPathEqualTo("/anything"))); } } |