Microservice is a software architectural style in which a software application is developed as a set of loosely coupled, small and modular services. Each service implements a business functionality & they expose their services to the consumer via a lightweight mechanism (usually REST API). Consumer here might not be the actual end users – It is usually application UI layer or another service.
As these services could communicate among themselves, there is a chance that changes in a microservice could affect the other services functionality. We will see how to overcome this challenge in this article.
To better understand the modern microservice architecture, lets first understand the old monolith architecture in which UI, business logic and data access layer – everything was tied together. We used to deploy a big fat EAR file on the server. So, any code change in a business functionality requires thorough regression testing of the whole application.
As you see, it is not very scalable! If one of the business functionality (say some of kind of file processing) consumes more memory/CPU, to support the load, we need more servers which runs everything as shown below.
In microservice world, each business module is created as a separate service. Any business functionality requirement change requires a code change in a specific service and testing of that service. It is easily scalable. So, maintenance/testing should be easier. right?
Yes, if the software design is as per the above image. However in reality, actual communication might not be like above image – mostly it would be as shown below where a service A might depend on another service B which in turn might depend on another service C. So, code changes in a service C could break other services (A & B) functionality.
As these microservices communicate among themselves, we can assume that there is some type of agreement among them. Because they know how to invoke another service, what to send in the request and what to expect in the response. We can call this a service contract! The issues arise only when contract changes.
In the above image, initially all these services were in Version 1. Service C was expecting ‘firstname‘ and ‘lastname‘ in the request to send a proper response to the calling service. Suddenly as part of a business requirement – Say Release 2, the service C is modified to expect ‘DOB‘ in the request. So Service C alone is modified – it has a different version (v 2) – all other services remain untouched!
If other services are not aware of this change, Service C broke the contract. It will definitely break any Service which depends on Service C. If the contract passes even after the code change, We can safely assume that a change in the service C will not affect other services depend on Service C. Usually this kind of issues can not be identified during the unit testing phase as it happens in the service end point.
Contract Testing is done assuming the service under test as a BlackBox – without worrying about the internal structure of the service! It is usually consumer’s responsibility to have a set of tests! Again I do not mean the actual end users as consumer here. Instead anything which uses these services is a consumer – it could be the UI layer or another service.
We do NOT do a very thorough testing of a service. Instead we ensure the request is accepted by the service and a proper response is sent within the accepted response time limit!!
TestAutomationGuru has already explained how to do REST API testing using JMeter in the below articles.
I would suggest you to read those articles to implement REST API testing for your current application. It is easy to implement a spreadsheet driven framework for the microservices testing as shown below.
In contract testing, Other than verifying the service response text – we also verify the response time (depends on your requirement). Lets assume, I have a contract test plan as shown here. I have assertions to verify if the response is as expected. One more assertion is to verify the if response is received within 100 milliseconds. So I use a duration assertion.
After executing the test, The result is as shown below – one of the services failed as it took more than accepted response time range!
In one of our applications, we have many microservices and we follow agile methodology for application development. We have thousands of automated test cases to do a system end-to-end testing. So, as these services are being modified on a daily basis, there is always a chance that service contract might break. Obviously it would break the end-to-end business functionality and test cases used to fail. We used to spend time analyzing these regression issues. In order to save time and get the immediate feedback on the services, before performing the automated regression in the continuous delivery pipeline, We need to perform microservices Contract testing to ensure that none of the services breaks the contract.
So, we proceed with the functional / performance testing of these services only when the contract passes.
Happy Testing & Subscribe 🙂