Traditional testing approaches are not always well-suited to web apps with a microservices architecture. This is no surprise because dozens of different services used by such web apps cannot be available for testing all at once. Our article will tell you about the specifics of microservices testing. We share our experience with how to build and implement the right microservices testing strategy and how to test microservices end to end without unnecessary waste of time and money.
Microservices Testing Types
Testing types used in microservices testing include:
- Unit testing This is when you focus both on testing the behavior of each service by observing changes in their state and taking a closer look at interactions and collaborations between objects and their dependencies.
- Integration testing helps make sure that each service operates successfully both individually and as a cohesive whole.
- Performance testing evaluates system responsiveness and stability under a certain workload.
- Component testing dives deep into each service’s code repository, testing all functions within that microservice in isolation.
- Contract testing looks at how data and messages are exchanged between microservices according to the rules, protocols, and expectations defined by the contract.
- End-to-end testing helps verify that the application as a whole meets all business goals.
7 Challenges for Microservices Testing
Microservice architecture gives software product owners many advantages. However, along with these advantages come corresponding challenges.
In particular, testing microservices requires specific approaches. Why can’t you do microservices component testing exactly like you used to do with monolithic applications? There are at least seven main differences in microservices testing:
1. Difference in scales
Besides endpoints for business logic realization, a microservice should include technical endpoints used for the communication of microservices with each other. Each endpoint should be tested, which requires the preparation of test data in a specific format.
Apps built on microservices can use dozens of different services. As a rule, microservices are developed separately and in parallel, which allows for sped-up development. That is why contracts are vital to such architectural solutions. Accordingly, the probability that all parts of the app will be ready for testing at the same time is negligible. Inattention to this feature can lead to violations to the schedule and budget of the software development project.
2. Chain reaction-type errors
Sometimes, it’s hard to find an initial error since a single user’s action may trigger a chain reaction inside multiple microservices communicating with each other. Possible causes of the errors could be as follows:
- An error caused by the issue in a microservice source code
- Wrong data transition between microservices
- An operation closed due to a timeout
3. Different communication channels between microservices
Some specific protocols may require additional skills for channel building. For the project to be stable, you must implement a system of contracts. Contract testing will make the integration of services as easy as possible, even if they are written by different developers or even different teams.
4. Microservices test automation
Automated testing is more commonly used for microservices, so it requires test scripts and autotest writing skills.
5. Data Management
Microservices typically have their own individual databases or data stores. Testing data consistency, data migrations, and ensuring proper data access and manipulation across services can be challenging. Techniques like database seeding, data versioning, or using shared test environments are often employed to address these challenges.
6. Monitoring and Debugging
With distributed systems, monitoring and debugging become more challenging. Identifying and diagnosing issues, tracing requests across multiple services, and capturing relevant logs and metrics can be more complex in a microservices architecture.
7. Integration testing
Integration testing is used in most developed software products to make sure that separate parts of the software are working together correctly. However, in an app with a microservice architecture, there are many more such parts than in differently designed software products. Microservices are designed for mostly autonomous operation.
Accordingly, each microservices component testing is done. The inclusion of each of them in the software requires testing the system for the compatibility of all its parts. Hence, the scope and role of integration tests are greater when testing microservices than for solutions with other architectures.
MobiDev project teams take into account the above-mentioned features when implementing microservices testing strategies for client software products. Let’s move on to talk about them.
Microservices Testing Strategies
Differences in testing strategies relate, as a rule, to the environments and instances used for testing. Based on microservice testing best practices, the following main types of strategies can be identified:
A full stack in-a-box strategy
With this approach, you test everything in one, isolated from the test environment. To do this, you need to replicate a cloud environment by creating the same virtual environment on the device or the cloud vendor’s infrastructure. In this way, you test the system in fairly realistic conditions, minimizing external influences due to isolation
Shared Test Instances Strategy
In this approach, you need to create a separate instance of a microservice that is shared by all members of the project team. Engineers work on their devices but, at the same time, use such a shared test instance as a reference point for their local environments. Thus, a separate shared instance of a microservice is always available for testing local builds.
Stubbed service strategy
Such a model requires the building of lightweight versions of specific microservices intended for testing. Dummy imitations created in this way simulate the behavior of real microservices in test scenarios without affecting dependencies.
To achieve efficiency in testing microservices, you need to rise above the purely technical aspects, realizing that the earlier a bug is detected, the lower the price of fixing it. Two approaches to building a quality control system in software development projects with a microservice architecture can be distinguished. With the first of them, we test everything completely in general, and only when a defect is detected, we dive into the details to understand what exactly fell and why.
Based on the second approach, we design a supervision system for our services right from the start of the project and proactively react to changes in status. This approach is more complicated to implement. In particular, a greater volume of testing communications between services. However, in business, overcoming obstacles often leads to extra rewards. The benefits of a proactive approach to testing microservices are the stability of the software system and the shortening of the bug lifecycle. For the effective implementation of such a comprehensive approach, it is worth applying observability.
Observability as a Vital Principle of Testing Microservices
Observability helps to measure a system’s current state based on the data it generates, such as logs, metrics, and traces. You use observation to understand what is happening in all environments and at the seams between technologies. With observability, you prevent issues from occurring or detect and resolve them in a timely manner.
It is crucial to adhere to this principle in performance testing, as observability provides transparency and the ability to see in real-time how the system behaves and prevent bottlenecks. You may see, for example, that you have a container that is demanding more resources than expected. This is not yet a bug, but it is a potential problem spot that you, due observability, can see to prevent issues.
Project teams usually implement observability using a combination of instrumentation methods. Most often, it may require another cloud service that we add to the system for constant monitoring during development. This helps to gain confidence in the stability and reliability of the software system.
When forming the technical stack of a microservice solution development project, it is advisable to give preference to those frameworks and tools that are useful for achieving observability. Then, you will see what is happening in the microservices during testing, and developers will receive valuable operational feedback. For instance, Zipkin, which our QA engineers apply, is a system designed specifically to trace calls between microservices and is particularly helpful when it comes to addressing latency issues.
In our experience, constant adherence to observability, while requiring additional effort and making development longer, at the same time contributes to the quality of microservice software and makes project processes more transparent, predictable, and manageable.
Microservices Testing Best Practices: Frameworks and Tools
It is quite expected that as the microservice architecture spreads, the toolkit for testing apps of this type also expands. Currently, there is a set of frameworks and tools that have proven themselves well in microservices testing. Here are some of them:
- InfluxDB is widely used to support performance testing of microservices. This time series database helps to aggregate and review metrics.
- Gatling is a performance-testing framework with which, in particular, it is convenient to create simulation tests and configure the corresponding scenarios
- Apache JMeter is a load testing, performance measurement and analysis tool. It is possible to create a large number of concurrent requests simulating the interaction of the system with a large number of users. The tool supports third-party plug-ins, which allow you to supplement its functionality with new custom features. Our QA engineers love to work with Apache JMeter for microservice testing.
- Hoverfly is a lightweight API simulation tool suitable for integration testing. Hoverfly is good for testing edge cases simulating, for example, network latency, random failures, or rate limits.
- SoapUI is a popular application for testing SOAP and REST APIs. Using it, you can conduct functional, load, and compliance testing. The tool’s capabilities include SOAP, REST, and HTTP(S) web services testing
- Postman is among the API testing tools often used for microservice solutions. This platform, in particular, provides opportunities for creating and executing requests to microservices and receiving responses. Postman has a companion tool, the command-line Collection Runner Newman. Newman is designed to run Postman collections and environments in a continuous integration (CI) environment. Its functionality makes it easy to incorporate a testing tool into a continuous integration (CI) pipeline.
- Pact is a distributed framework for integration testing of microservices. In contract testing, it can be used to verify the compatibility of web apps and APIs and make sure that the software meets the criteria of the customer-driven contract.
The choice of frameworks and apps for testing, as always, is determined by the specifics of the software product, the tech stack, in particular, programming languages, and the possibility of integration with CI/CD. For example, the project team writes the code for your app in JavaScript and, as often happens, uses Node.js on the backend. In this case, it is advisable to pay attention to the possibility of using the Mocha test framework, the assertion library Chai, and the Node.js library SuperTest for API testing. The Cypress front-end testing tool is often used for the client side.
More often than not, microservices will be isolated in Docker containers, so the QA team needs to be able to work with it as well. A lot of helpful microservice testing tools are related to containers. For example, the TestContainers framework substantially facilitates integration testing. With TestContainers, it is possible to create and run throwaway, lightweight instances of, e.g., databases or software modules in the form of Docker containers. Such containers are destroyed after the testing is completed.
A microservices-based application fairly often is a system that comprises a simple user interface and a complicated backend under the hood. A QA engineer, who tests microservices, must be experienced in using Docker and console utilities for gathering logs and connecting to containers. Being skilled in programming is a plus.
Meanwhile, one of our main messages to you is that testing microservice apps differs from testing monolithic ones by approaches, not by tools. Depending on the technical stack of the project and testing goals, you can use the same frameworks as for traditional architectures to test, for example, a separate service or API. However, these tools should be used following the specifics of microservices testing that we described above.
Our QA engineers, in particular, pay due attention to ensuring Observability. Observability can be implemented with the help of the following tools. For instance, Zipkin is a system designed specifically to trace calls between microservices and is particularly helpful when it comes to addressing latency issues.
There are other, more specialized tools that you can also use depending on what you’re trying to look at at the moment. Options like Raygun APM offer both instrumentation and data collection processes as well as a comprehensive dashboard that you can use to visualize the data your metrics are creating. Tools like these are test result reporters that allow you to see the test coverage of each scenario, provide better visibility into the running pipeline, and more.
MobiDev Case Studies for Testing Microservices
The case studies given below will help you understand how to test microservices end-to-end in practice. The projects we are talking about are characterized by a high level of microservices test automation, the inclusion of testing in CI/CD processes, and the close interaction of quality control specialists with the rest of the software development project participants.
MobiDev Case Study #1: PORTAL FOR MANAGING INTERACTION WITH CLOUD SERVICES
Project Overview
The product is a portal for developers, DevOps engineers, CTOs, and product owners. The purpose of the software product, developed by MobiDev, is to facilitate the management of interactions with leading cloud services such as Amazon, GoogleCloud, Azure, etc.
For such a web resource, it is fundamental to collect all the functionality of cloud solutions together. This approach allows you to optimize the use of resources and, therefore, the cost of using them.
Tech solution
A microservice architecture was chosen for product development, as the functionality was fragmented, and each service was aimed at certain tasks. The solution is high-performance for data gathering and preparation. In addition, machine learning features using Python and Go for model training and refinement were integrated into the product.
Microservices testing
The QA team carried out API testing and performance testing. API tests made it possible to verify the functionality of individual components within a single application. Also, the testers monitored the interaction between the containers using logs to make sure that the system was running smoothly and stably. The team worked with Docker, Pytest, and a custom framework specially developed by in-house specialists for this project.
We set up the CI/CD pipeline to automate the development and testing processes. It allowed us to test the new version for bugs and errors quickly.
MobiDev Case Study #2: FACE & VOICE RECOGNITION AND AUTHENTICATION SOLUTION
Project Overview
A US-based company entrusted us with the development of an enterprise verification-as-a-service (EVaaS) solution for securing access to sensitive data. The goal was to build a microservice-based single sign-on (SSO) software that enables biometric authentication using face and voice recognition.
Tech solution
We proposed a microservices architecture for the software product.
Each microservice becomes a separate subproject with its functionality, which makes it easier to write, support, and enhance. This approach allowed us to choose the optimal tech stack according to the goals and specifics of each microservice.
The biometric authentication flow includes facial capture and voice recognition. An off-the-shelf WebRTC service was implemented as there was a need to process media data to the server.
The product also relies on machine learning for biometric recognition. The system may identify the user based on voice, photo, and questions.
Microservices testing
In our case, along with the creation of automated scripts, testing assumed the necessity of gathering and preparing a large dataset for model learning and testing.
Different software modules require different datasets depending on tasks. For example, when testing a voice recognition service, voice samples were recorded on various mobile and desktop devices, involving many people of different ages and genders.
The dataset for driver license recognition was formed with data collected by external testers who used real IDs. It was challenging to classify the driver’s licenses by all applicable standards for all U.S. states.
Automated testing was applied with created test datasets, where an expected output was generated, and the data was sent via scripts. As a result, a report, which included the information for each endpoint and test status, was created.
How We Test Microservices at MobiDev: 7 Rules
By far, one of the most important principles of monitoring microservices involves treating each service as its own software module. Our QA engineers approach every microservice so that it is capable of correct operation completely independently of everything else. After making sure that the results of microservices component testing in isolation are satisfactory, we then verify that the system works well together.
- Every single microservice is tested separately with API tests to ensure the fast identification of initial errors.
- Integration testing is conducted to ensure proper communication between microservices.
- End-to-end app testing is conducted via an API / User interface to ensure that all of the integrated parts function properly.
- At the microservice level, the code is covered by unit tests to ensure the proper functioning of the microservice modules.
- Event and error logging is used for rapid responses in case of unexpected problems.
- The involvement of test automation specialists and the collaboration of QA engineers and developers allows for the effective optimization of testing.
- Observability, which is a crucial element of our microservices testing, strengthens the stability and reliability of web apps.
MobiDev has extensive experience in building and testing microservices. With in-house Solution Architects, software engineers, UX/UI designers, DevOps specialists, and QA experts on our side, we can support you throughout the entire development process.
Feel free to contact us if you need help with creating the most effective testing strategy for your microservices project.