How To Test Microservices Effectively

This is a fact that microservices have become very popular in the last few years. Many development teams are choosing microservices over monolithic structures. Let me just quickly remind you what it is:Monolith is when all the code is bundled and deplo…


This content originally appeared on Bits and Pieces - Medium and was authored by Denis Peganov

This is a fact that microservices have become very popular in the last few years. Many development teams are choosing microservices over monolithic structures. Let me just quickly remind you what it is:

  • Monolith is when all the code is bundled and deployed as a single application.
  • Microservice application is a group of distributed programs that communicate over networks, occasionally interfacing with third-party services and databases. That means each service is a separate unit with its own logic and database that communicates with other units through APIs. With microservices, multiple teams work on independent services, enabling you to deploy more quickly.

Testing a monolith product means testing one application, and it’s relatively easier than testing a bunch of applications in a microservices architecture since they provide more points of failure than a traditional monolith. Furthermore, testing microservices might come up with more challenges compared to monolith:

  • Microservices are usually deployed across multiple servers and geographical locations, which adds latency and exposes network disruptions of the application.
  • Developers are free to deploy their changes at any time since each microservice is independent, so sometimes it’s more difficult to keep your autotests up-to-date.
  • Since each microservice exposes at least a few API endpoints, there are more test scenarios to cover.
  • Microservices are independently-deployable and sometimes are built by different teams, therefore additional checks are required to assure that each microservice will still function correctly with other services when deployed.

However, microservices-based applications have many great advantages which I will cover in this topic, but let me clarify one important thing first:

Why do we test?

  1. The software development process is iterative, so we have to test the software to be sure that the recent changes have not broken anything.
  2. Verify that the application is doing what it is supposed to do. We write the tests based on the requirements and validate that the actual results are matching the expectations.
  3. Tests run automatically whenever source code changes and notify the team if they fail. It increases confidence in our product and saves development team’s time.

For example, imagine that you have 5 development teams working on different components of your product. Many developers deploy new code with software changes multiple times a day. Without proper testing, you will most likely experience some side effects, because you weren’t aware of the changes made by other teams. After all, it will get very complex and in case of any mistake, the rollbacks are usually quite significant. It is just like a dependency tree, where taking out one of those microservices from the whole system might be difficult, because of such a component, you will probably need to revert other deployments, which are dependent on this microservice as well.

Also, the need to include team members to work on different parts of the system means that things are about to get more and more complex. Creating dependencies means spending more time and more money to collaborate continuously. There might be extra costs for additional tools if you don’t want to develop them by yourself. Furthermore, you will probably need to spend some time and resources to get your team onboard and align your development and testing approaches.

However, testing at lower levels has many advantages in the long run:

  • Great savings in time and regression. Most of the time, one of the goals of automation is to cut down the testing time, ideally, down to a couple of minutes. Usually, the build time lasts for several minutes, and once we deploy it to the environment, the tests should be running within several minutes. Overall, the test time should take no more than approximately 10 minutes. Achieving that goal is more than possible when you test microservices and it will be a huge win when you consider how much time it takes for an automated environment to test the whole subsystem.
  • Catching bugs earlier. This might be obvious, but the earlier you catch a bug, the faster and cheaper it is to fix. The cost of not catching a bug as early as possible can have real financial consequences for your business, especially if you consider the damage it could do while any of your services are down.
  • Confidence in refactoring or expanding a system. While expanding or refactoring your system, you can have confidence that if anything will go wrong or break, you will be notified right away. Even before it goes to production, so you don’t get questions from customers regarding something which is broken or not working. Your product confidence will increase greatly when you are resolving issues before it reaches production.
  • Standardization on a company level. Even if in the beginning I mentioned that this is a disadvantage, however, if done everything correctly, the advantages outweigh the disadvantages. Transitioning may be challenging but once you do that, everything becomes much easier from the perspective of management. Your teams can be shuffled with no issues, as well as people could be motivated to change teams if they find different domains more interesting. Since all teams are working under the same standards and principles, using the same tools, your engineers will become valuable assets for different teams within your organization with no doubts. To make the most of standardization, you can also make use of an open-source toolchain like Bit which provides a visual workspace for teams to collaborate on development and testing and a centralized repository for sharing and reusing components across microservices with automatic semver. This ensures that each microservice integrates seamlessly with other services, thus streamlining development and reducing the time and effort required to keep each service up-to-date. You can learn more about it here, and here.

When designing your automated test coverage, keep in mind the test pyramid as a way of distributing your tests. There are many different versions of testing pyramids. According to my professional experience, the below version of it suits testing microservices-based applications just perfectly. However, the main goal is to make sure that every business logic and piece of code is tested, so it may differ from the way you test your microservices.

The further down the pyramid you can push your tests, the earlier you can get feedback, which makes it easier to address any bugs. At the bottom of the pyramid, there are tests that are fast, easy to develop, and cheap — unit tests. The further up we go, the more complex tests get. In addition to being more expensive to develop and maintain, such tests are running slower as well. Component tests usually have more lines of code. Software elements in these types of tests often interact with something else (other services, databases, etc). And then we have E2E tests, which can check your whole system or subsystem.

  • The goal of unit testing is to ensure that the smallest portion of each service works as expected. Unit tests check each of hundreds of small functionality components individually and independently. It’s most of the time done by developers and I don’t think you need to know more as a tester. However, during rushed development, the quality assurance team may be required to perform unit testing after the developers completed their work on the project.
  • Component testing ensures that each service can perform its task, and when the service changes internally, consumers will continue to receive correct results from that service. Component testing verifies that a given service is functioning correctly by isolating it within the system. The service itself thus remains a black box. To run component tests, services are called independently and responses are verified. If there are service dependencies, they must operate as stubs that enable a service to function, but without interacting with other services. This will also prevent erratic behavior caused by external calls, putting the focus on testing a single service.
  • End-to-end testing is the final testing stage that involves testing an application’s workflow from beginning to end, for complete end-user “journeys”. The E2E suite should cover all the microservices in the application using the same interfaces that end-users would. During E2E testing the application is treated as a black box, while tests exercise the system as it is fully deployed. Therefore, the application should run in an environment, which is as close as possible to production. End-to-end tests can help you ensure there are no gaps between microservices and all work towards achieving their goal.

Do we always need all levels of testing?

I wouldn’t surprise you if I just say it depends on the software you are implementing. But if your system or subsystem will play a critical role in business, then the answer is yes — you definitely need all levels of testing. Otherwise, you can consider skipping some layers. However, you will most likely build layers upon layers, therefore tests are not that expensive, since you would have many implemented tests in place already. The more familiar you are with those test types and techniques, the easier it is to follow and keep improving your product.

As you move up the testing pyramid, you’ll need to run tests that involve multiple services. And this is where a CI/CD pipeline becomes invaluable.

There are various CI/CD tools available that take care of triggering builds, running tests, and providing feedback using alerts and dashboards. When configuring your CI/CD pipeline, you need to reach a balance between checking that everything still works after each commit and providing feedback to your development team as soon as possible. The problem might occur when running your automated tests takes over an hour, then by the time the build breaks and the dashboard notifies you about failing tests, the developer who made the changes have already moved on to some other task. To really feel the benefit, the system needs to deliver feedback in several minutes or in the time it takes to get a cup of tea. That way, if something goes wrong you can jump in to fix it without a delay. A fast turnaround time also motivates the team to commit often.

The benefits of a fast CI/CD process for microservices are not limited to reacting to failures and fixing issues quickly. Getting working application in front of users as soon as possible makes it much easier to get their feedback and understand how they behave in the “real world”. By being able to deploy services independently, teams can deliver value to users by making incremental and regular improvements based on real usage.

However, a common approach is to run a nightly job that runs the full suite of tests and triggers a limited set of tests after each commit. But, as I mentioned before, when you test microservices, you can cut down on the testing time by several minutes, which is a great benefit.

Even with a highly developed CD/CD pipeline and many testing layers, it’s unlikely that nothing will ever go wrong in production. For advanced and complex systems, it’s impossible to test all potential circumstances and combinations that could arise in a live microservices system. By having an automated and reliable process for delivering software changes, you can react quickly when something goes wrong.

In order to address issues in production quickly, you might want to proactively monitor your system for issues as well. Furthermore, if you run tests in production, such as canary releases or some chaos engineering, some form of monitoring is also essential for observing your experiments and reacting to the results quickly.

With a monolithic application, if something breaks in production, the usual action is to roll the changes back. While this is also an option with microservices, having a dependable and fast deployment process means it’s also possible to get a broken piece of code out quickly and roll forward. This option is especially attractive if the last release involved database schema changes, which makes reverting a more difficult process.

In my project’s CI/CD pipeline, we run our tests after each commit in the following order:

  1. Unit tests
  2. Component test
  3. E2E tests

For example, when a developer makes changes to any service, as a part of our CI/CD pipeline the first tests which we will run are Unit tests. When unit tests are passed, our component tests run will start. And only if unit and component tests are finished with no failures we will run our E2E tests by pulling the latest images of other services.

Single or multiple repos?

The usage of microservices raises several such kinds of important and interesting questions. So should each microservice have its own repository, or is it better when all microservices of an application are stored in a single, monolithic repository?

Of course, there is no right answer to this, but I suggest you go with a single repo. It might be difficult at the start, because single repo may cause risks related to more complexity and a bigger amount of potential merge conflicts, but you will feel the benefit later, since it makes reuse, discovery, and standardization of your project much easier.

On other hand, separate repos can help to enforce a loosely coupled system and make the ownership of individual services clearer. However, when each microservice has its own repository, it is much more difficult for any person to understand how the whole system fits together.

In addition to what has been already said, it may be good for you to go with separate repos first when you start adopting microservices in order to implement a decoupled model, and later move to a single repo when you get more demand for reuse and standardization.

Best microservice testing practices:

  • It’s important to determine the fundamental links in the architecture and always test them.
  • Test each microservice as one software module. In microservice architecture, every service is considered a black box, therefore try to test each microservice similarly.
  • Don’t just test happy path scenarios. Microservices can fail and it’s important to simulate failure scenarios to build a more stable system.
  • Don’t stop to do manual testing. It’s important to perform some corner-case checks manually as well, but try to keep them to a minimum and on an exploratory basis.

Conclusion

The fundamentals of microservices testing are not new compared to traditional web-services testing, but the importance of doing it has only become more critical in modern systems. Described models were used in my teams and we did our job very well, as I wish you will.

Build apps with reusable components like Lego

Bit’s open-source tool help 250,000+ devs to build apps with components.

Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.

Learn more

Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:

Micro-Frontends

Design System

Code-Sharing and reuse

Monorepo

Learn more


How To Test Microservices Effectively was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Bits and Pieces - Medium and was authored by Denis Peganov


Print Share Comment Cite Upload Translate Updates
APA

Denis Peganov | Sciencx (2023-02-09T15:40:04+00:00) How To Test Microservices Effectively. Retrieved from https://www.scien.cx/2023/02/09/how-to-test-microservices-effectively/

MLA
" » How To Test Microservices Effectively." Denis Peganov | Sciencx - Thursday February 9, 2023, https://www.scien.cx/2023/02/09/how-to-test-microservices-effectively/
HARVARD
Denis Peganov | Sciencx Thursday February 9, 2023 » How To Test Microservices Effectively., viewed ,<https://www.scien.cx/2023/02/09/how-to-test-microservices-effectively/>
VANCOUVER
Denis Peganov | Sciencx - » How To Test Microservices Effectively. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/02/09/how-to-test-microservices-effectively/
CHICAGO
" » How To Test Microservices Effectively." Denis Peganov | Sciencx - Accessed . https://www.scien.cx/2023/02/09/how-to-test-microservices-effectively/
IEEE
" » How To Test Microservices Effectively." Denis Peganov | Sciencx [Online]. Available: https://www.scien.cx/2023/02/09/how-to-test-microservices-effectively/. [Accessed: ]
rf:citation
» How To Test Microservices Effectively | Denis Peganov | Sciencx | https://www.scien.cx/2023/02/09/how-to-test-microservices-effectively/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.