Practical Tips for Mocking External Services Using WireMock
I work at BuySell Technologies Co., Ltd., and I am involved in developing a system that manages listing and ordering across different e-commerce websites. Our product has a lot of integrations with external services such as eBay and Shopify, and I wanted to mock them in the development environment.
I achieved this by building a stub server in the development environment with WireMock. Below I explain the details of my solution, and offer some practical tips that helped me make the most out of WireMock.
Motivation
In our product, we often integrate with external services. Each service provides us with a test environment for the purpose of developing an API integration, but basically we are limited to a single environment. My team has three environments: production, staging and development. Our production environment is integrated with the production environment of the external service, and the staging environment is integrated with the test environment of the external service. As mentioned earlier, there is only one test environment provided by the external service, and using it for both staging and development risks data inconsistencies.
As a result, we chose not to integrate the development environment with the external service. This caused API integration-related processes to generate errors, leaving the development environment virtually unusable.
To solve this problem, we decided to build a stub server using WireMock and integrate from our development environment.
WireMock is an OSS that makes it easy to build stub servers; it serves as a Java application and allows you to define mocks in JSON files.
We decided to use it mainly for the following reasons.
- Mocks can be defined in JSON, so there is no need to write code
- Mocks can be defined flexibly with features such as Request Matching and Response Templating
- We need mocks in JSON, XML, and GraphQL, and Wiremock can handle them all
- Docker images are distributed
Basic Usage
In this article, I will use Docker as an example.
Directory Structure
WireMock uses /home/wiremock as its root directory, from which it refers to two subdirectories, mappings and __files.
The roles of each directory are as follows.
mappings
- Stores mock definition JSON files
__files
- Stores mock response definition files
- Responses can also be defined in the JSON file under mappings
- Use when you want to cut out the response into a separate file
The directory structure of our project looks like this:
We define a Dockerfile and docker-compose.yaml to mount the project's ./wiremock to the container's /home/wiremock.
Mock Definitions
The JSON file to mock POST requests to /sample is shown below.
Case of separating the response into a different file.
Case of not splitting the response into a different file.
Practical Use Cases
Here are some practical use cases we have encountered in the process of actually building a stub server.
When query parameters are included
If a path is specified with urlPath, it must be an exact match. Therefore, if a query parameter is included, it must be specified with urlPathPattern, not urlPath.
Example of a query with a query parameter named page
Return response dynamically
In some cases, you may want to return a response dynamically rather than with a fixed value. This can be achieved by using a feature called Response Templating. By enclosing the response in double brackets ({{}}), various helpers can be used, including conditional expressions such as if.
Response Templating is off by default, so if you want to use it, you must pass the option when launching the container.
Here are some helpers that we’ve found particularly useful. Since quite a lot of helpers are provided, please refer to the official documentation if you are interested.
Example of returning a random value
Example of generating a response using a request value
The following is an example of returning a response using an array of objects called items in the request body.
Dynamically return a response after splitting a file
Response Templating can be used even if the response file is split. However, if there is an evaluation expression in the response, it will contain unnecessary newlines. In such cases, the trim helper or tilde (~) must be used to remove blank lines.
The trim helper removes leading and trailing whitespace and blank lines.
The tilde can be placed at the beginning ({{~) or at the end (~}}) inside the braces. If it is placed at the beginning, it removes the blank line immediately before the template block; if it is placed at the end, it removes the blank line immediately after the template block.
When not deleting blank lines
Using each helper in the response file will generate an empty line before and after each block as well as at each iteration within each block.
When using trim helpers and tildes
Use the trim helper to remove blank lines before and after each block, and use the tilde to remove blank lines between elements within each block.
Mocking GraphQL
In the case of GraphQL, unlike REST, there is only one endpoint, so it is not possible to define a mock for each endpoint.
When sending a request to a GraphQL endpoint, the operation name is defined and included in the request according to best practices.
Therefore, we used bodyPatterns to define a mock for each operationName in the request body.
Mocking XML
In the case of XML, there is no helper like jsonBody that returns an XML response.
Hence, we need to create an XML file that will serve as the response and specify it with bodyFileName.
Also, as with JSON requests, you can use the XML of the request in Response Templating.
Using proxying when WireMock cannot define a mock
We were able to define mocks for all the APIs we needed using the methods described above. However, as the number of external services to be linked will continue to increase, there is a possibility that some APIs will be difficult to mock with only WireMock's standard features.
To prepare for this kind of situation, we decided to use the "Proxying" feature.
By using this feature, requests accepted by WireMock can be proxied to another host, and the response returned from that host can be returned to the caller. This allows you to build a stub server in any language you like, allowing you to define responses with a lot of flexibility.
However, server maintenance could be trouble and the advantage of being able to easily define mocks in JSON is lost.
For this reason, we decided to use Proxying only when it is impossible to define a mock using only the standard WireMock features.
Conclusion
In this article, I have introduced how to build a stub server using WireMock, with practical use cases. Although there was a wide range of APIs that needed to be mocked, WireMock's rich features made building a stub server easier than I had imagined!
I highly recommend it to anyone developing a product that has integration with external services, so please give it a try.
/