QA testing for microservices
Tips for building a low cost, high efficient, and automated mock system
By Eddie Ying
This post is also available in Korean.
The main goal of QA testing is to identity any bugs in the software and help to release the product successfully. While there are many types of testing like functional tests, interface tests, and performance tests, the fundamental stays the same. The process of testing is crucial to ensure software quality. We need accurate, relevant, and high-quality data that covers all scenarios. We must also use generated mock data instead of production data to ensure stability but consider the efficiency of creating and managing such data. We may also need to do so for high-level services that tend to have many dependencies and strongly coupled with other services. It may feel like running into a wall.
In this post, we will share some ideas about building a mock system that is low in cost and highly efficient for services with many dependencies and frequent changes.
Mocking in a microservices environment
Today, many companies have a microservice architecture and implement contract testing, which is a way of testing that two microservices communicate with each other according to the agree contract. Development takes place independently in compliance with the contract, prior to joint debugging or integration. Commonly, a mock service is used to produce rich data based on the contract for the corresponding provider or consumer so that it can cover all branches of the code and ensure the quality of service.
This is a relatively easy and highly efficient testing method for lower-level services with low dependencies. However, what if we had more than 10 lower-level services with dependency on higher-level services? It would not only require a huge investment of time and resources to create a mock of all 10 services, but also require high maintenance costs for responding to changes. We need a more effective testing method for higher-level services; and one solution would be to mock an interface of a service without affecting other interfaces.
API gateway is the way
API gateway is a reverse proxy and a single entry point for all calls. Capturing requests and returns here would enable dynamic handling and enhance flexibility.
However, this sounds good in theory but is not practical to implement for mature frameworks in production. There is just too much cost and risk involved for modifying the existing code and architecture that has been tested and verified. A better solution would be to add a small block to this building blocks of architecture.
As shown in figure 3, a dedicated gateway like Django can be added to the existing test link. As long as the API gateway continues to handle all routing and forwarding requests, we can intercept requests and returns en route and edit the returns as mock data.
Types of mock data
Here are some types of mock data you can consider to generate or modify.
● Full returns: During the development, a new interface is needed for a dependent service. If the interface is being developed in parallel, mocking the entire interface of the dependent service could be a great option.
● Interface abnormality or return delay: This can be used for destructive testing and for testing to process delayed returns of an interface.
● Adding a partial field to the interface that responds: This is used to add a new field to a depending interface.
● Modifying the content of the field returned by the interface: Test data requirements can be satisfied by modifying the values to be returned by the required field. The benefit of partial modification compared to the full mock of the interface is that mock data doesn’t have to be adjusted every time.
● Deleting fields returned by the interface: This can quickly meet the test requirements in a test to validate a certain field of the interface.
● Modifying the body of an interface request: At times, you may need to modify the content of the requested body dynamically to meet the test requirement.
● Modifying the header of interface request: Add, modify, delete the requested header dynamically.
Grouping requests
It’s possible to have multiple requests come to a test server concurrently by multiple users, such as a when conducting a normal functional test and an automated test. To identify which request to mock, you can consider the users’ UIDs to the request header. When a request calls a dependent interface, add a UID to the request and group them based on the UID. Then you can use the requested URL, method, request parameter, and body to identify the specific interface to mock.
Conclusion
In this post, I wanted to share some ideas about how you can implement a mock system of this kind. I hope that this post will provide a starting point for developing a system that creates test data for higher-level services with a perspective on time to market, cost, and risk.
We’re actively looking for passionate individuals who are not afraid to ask questions, challenge the norm, and make things happen. Check out our open positions or sign up for job alerts!