Testing strategy to achieve continuous delivery in Microservices — Part 2
In Part 1 we discussed how to build our test strategy for continuous delivery in a microservice architecture. In this part, we will discuss in detail what each kind of test should cover.
Unit Tests
Unit tests are supposed to test one layer at a time. Any dependencies with in the class should be mocked or stubbed. They are very fast to run and provide immediate feedback. Also, they are faster to debug and identify issues. I suggest to employ Test Driven Development (TDD) to write high quality unit tests. Other than verifying the behavior, unit tests provide code documentation and improve code modularity.
Following are some of the best practices that can be employed while writing unit tests:
- Write separate unit tests for each scenario with in method.
- Test name should be descriptive and clearly specify intent.
- Don’t write separate tests for private methods. If there is too much logic getting tested in a method, evaluate how code can be modularized further.
- No need to write separate tests for Pojos and utility classes. These should be covered with in the service classes tests.
- Avoid writing tests for generated code. However, you can keep few test cases to verify intended behavior.
Integration Tests
Since unit tests only cover one layer at a time, integration tests are required to ensure that multiple layers together are working as expected. Integration tests are different than end to end tests as they don’t try to cover the whole integration but only one or two layers integration at a time.
In microservice, I suggest to employ integration tests at Resource/Controller level:
- Have api tests for each api covering atleast one success and one or more failure scenario.
- Mock external services utilizing REST contracts for providing their behavior.
- Better not to mock database layer
Some people suggests to have integration tests only and avoid unit tests for microservices as the services are usually small and have crud behavior mostly. From my experience, I see services might initially be simple crud but they usually grow and then it becomes difficult to manage service based on integration tests only. So, depending on service scope, this call can be taken. I believe it depends on service to service. For some microservices, this can be done and indeed a better option to have.
Contract Tests
With unit tests and integration tests, we covered most of the code written by us. However, we are relying on mock contracts for internal and external services. To ensure provider service doesn’t make any change that can break our service, contract tests need to be written. Contract testing should check for success and error response verifying the returned object is as per the contract while avoiding too much of business validation testing. Also, it should avoid benchmarking of service. These tests can be run on scheduled basis.
Another option is to have consumer driven contracts (CDC). In this consumer service provides a test suite to provider. Provider service can run this suite before going live to ensure its new changes don’t break consumer.
Service Api Tests
One of the critical piece of testing in microservices world is api testing for the service. Though we have covered service via unit and integration testing, the end behavior of service needs to be verified. Most of the times different teams are working on different microservices within a product and it does make sense to have well defined contracts between them. Also, many times the microservices are directly exposed to the external world. So, an end to end testing must be performed for the microservices to verify desired behavior.
Service api testing doesn’t rely on mocks but instead verify the actual behavior of service. Sometimes few services are working together for a functionality as a cluster and are released together, in those cases we can define service api tests for those services together.
End to End tests
End to end tests also called functional tests cover the whole system, user journeys and verifies the system is behaving correctly end to end. They give the maximum confidence as they test the complete integration. Since they are mostly written by QA, they also provide another eye to the functionality developed. Functional tests cover the user journeys and verify how the system is behaving with regard to user. They give the most confidence when we need to decide whether our code is ready to be shipped. However, these are slow to run and thus it might not be possible to run them all before making any new release. A good functional test suite should ensure to cover all the major user journeys while relying on other layers to cover the detailed scenarios .
Non functional Tests
Another category of tests include non functional tests. Theses include performance testing, load testing, security testing etc. Depending on the application requirement, these can be built and integrated in the pipeline.
Originally published at http://www.ankitbansal.net on July 4, 2020.