In the previous article, you could see how using a mocking framework like Mockito can help you address isolation challenges, which is the first of the two types of issues development typically face when dealing with dependencies in testing. The other issue occurs when you actually want to involve dependencies to be part of your test scope (i.e., for integration and end-to-end testing purposes), but these dependencies pose limitations with regards to their availability or the ease with which the behaviour required for your test can be triggered.
Tutorial Chapters
- Increasing Test Efficiency with Simulations & Service Virtualization (Chapter 1)
- Isolating Components in Unit Tests with Mockito (Chapter 2)
- You’re here → Getting Started with Service Virtualization using WireMock (Chapter 3)
- Creating More Sophisticated Simulations with WireMock (Chapter 4)
Similar to the example we have seen in the previous article, imagine that we’re building a web shop, but this time we follow the ‘buy before build’ principle and decide to use a third party payment provider service. As part of our testing activities, we want to test that our web shop upon checking out an order sends a request to the payment provider service, that the payment service provider can successfully process this message and that our web shop processes the payment provider response correctly and marks the order as paid. Also, we want to check that our web shop is able to handle any erroneous messages that the payment provider might send, as well as temporary outages or long delays in responding times, correctly, i.e., without it affecting the consistency and correctness of the order processing flow.
Now, we could technically use a real payment provider to use in our testing, but this poses some challenges:
- We don’t know if there’s a test environment available, how it behaves and whether or not we are its only users.
- If a test environment is available, we might have to pay a fee to use it.
- It is potentially hard to simulate edge and error cases using a third party service on demand.
As we have discussed in the first article, using a stubbing or service virtualization solution provides us with the means to deal with these challenges.
In this article, I will be using WireMock to build a simulation that exerts the exact behaviour we need for our integration and end-to-end tests. WireMock is an open source simulator for HTTP-based APIs, written in Java. It comes with a lot of features out of the box, and it goes beyond the scope of this article to give a complete overview of everything you can do with WireMock. However, the scenarios and use cases I will discuss in this article and the next one should give a good overview of how WireMock and similar tools can help you address challenges posed by your dependencies when you need them for testing.
In the examples in this article (and the next one), I’m defining the behaviour that WireMock exerts in code, but it’s also possible to do this using JSON mapping files. Also, in the code base associated with this article series, I’m starting the WireMock server just before the tests are run and automatically stop it after the test run ends by means of a JUnit rule. There’s also an option to start and stop WireMock yourself in code, as well as the option to run it as a standalone process. More information about this can be found in the ‘Getting started‘ section of the WireMock documentation.
Let’s start by defining a mock that responds to a POST call to the /checkout endpoint with an HTTP status code 200:
private void receivePostToCheckout_RespondWithHttp200() { stubFor( post( urlEqualTo("/checkout") ) .willReturn( aResponse() .withStatus(200) ) ); }
One of the things I like about WireMock is that its code is written using a fluent interface, with readability in mind. All definitions follow roughly the same pattern: First define the specific properties that the request has to match, then specify the characteristics of the response to be returned when there’s a match.
Note: the GitHub repository that contains the code examples shown in this article series also contain tests that invoke the WireMock stubs, so you can use those to test and play around with the stub definitions on your own machine.
The JSON equivalent of this responder definition looks like this:
{ "request": { "method": "POST", "url": "/checkout" }, "response": { "status": 200 } }
For all subsequent examples, I’m not going to include the JSON counterpart. It is good to know, though, that all WireMock features are available no matter if you’re writing your stubs in Java or in JSON, they’re equivalent in terms of features provided.
Of course, the example above does not reflect a realistic situation. In production-like systems, API requests and responses are often more complex, and to simulate their behaviour we need WireMock to offer more advanced features. The next example shows you how to configure a stub that only responds to incoming POST requests to /checkout that contain:
- Basic authentication with a specific username and password
- A specific cookie with a specific value
- A request header specifying that the request content type is ‘text/plain’
private void receivePostToCheckoutWithAdditionalRequestProperties_RespondWithHttp200AndTextBody() { stubFor( post( urlEqualTo("/checkout") ) .withHeader("Content-Type", containing("text/plain")) .withCookie("sessionId", equalTo("abcde12345")) .withBasicAuth("john","supersecret") .willReturn( aResponse() .withStatus(200) .withBody("Checkout completed successfully."))); }
These features allow you to be much more specific as to which requests WireMock should respond, and it enables you to respond to different requests with different responses. In the final article in this series, we’re going to see how you can create simulations with even more realistic behaviour for example by simulating delays, fault responses and creating stateful stubs.
All the code samples from this article (and the other articles in this series) can be found on this GitHub page: https://github.com/basdijkstra/testproject-mockito-wiremock.
See you in the next article! 😎