Microservices involve breaking down functional areas of code into separate services that run independently of each other. However, there is still a dependency in the type and format of the data that is getting passed around that we need to take into consideration. If that data changes, and other services were depending on the previous format, then they will break. So, you need to test changes between services!
To do this, you can either have brittle end-to-end integration tests that will regularly need updating and are semi-removed from the process, or you can be smarter, and just test the individual services continue to provide and accept data as expected, to highlight when changes are needed. This approach leads to much quicker identification of problems, and is an adaptive approach that won’t be as brittle as integration tests and should be a lot faster to run as well.
What I’m proposing is to integrate contract-based testing. (Note, we are only in the early stages of trying this out at my work)
Here’s how it works:
Service A is providing Service B with some sort of data payload in the Json format X. We call Service A the provider and Service B the consumer. We start with the consumer (service B) and determine what expectations it has for the data package X and we call this the contract. We would then have standard unit tests that run with every build on that service stubbing out that data coming in from a pretend ‘Service A’. This means that as long as Service B gets it’s data X in the format it expects, it will do what it should with it.
The next part is to make sure that Service A knows that service B is depending on it to provide data X in a given format or with given data, so that it if a change is needed, Service B (or any other services dependent on X) can be updated in line, or a non-breaking change could be made instead.
This is Consumer-Driven contract testing. This is nice, it means that we can guarantee that Service A is providing the right kind of data that Service B is expecting, without having to test their actual connections. Spread this out to a larger scale, with 5 services dependent on Service A’s data, and Service A giving out 5 types of data to different subsets of services and you can certainly see how this makes things simpler, without compromising effectiveness.
A variation of this is to have Service B continue to stub out actually getting data from Service A for the CI builds. But instead of testing on Service A that it still meets the expected data format of Service B, we can put that Test on Service B as well, so it also checks the Stub being used to simulate Service A against what is actually coming in from Service A on a daily basis. When it finds a change, the stub is updated, and/or a change request is made to service A.
Both types have advantages and disadvantages.
Writing these sort of tests can be done manually, but there are tools which help with this as well, making it easier. Two such products are Pacto and Pact. They are both written in Ruby, Pacto is by Thoughtworks and Pact by RealEstate.com.au. Out of these 2 I think Pact is a better option as it appears to be more regularly updated and have better documentation. PactNet is a .Net version of Pact written by SEEK Jobs which is the language used at my work, and so is the solution we’re looking into.
These tools provide a few different options along the lines of the concepts described above. One such use case is that you provide the tool with an http endpoint, it hits the endpoint and makes a contract out of the response (saying what a response should look like). Then in subsequent tests the same endpoint is hit and the result compared with the contract saved previously, so it can tell if there have been any breaking changes.
I’m not sure how well these tools go at specifying that there might only be part of the response that you really care about, and the rest can change without breaking anything. This would be a more useful implementation.
Note that most of the writing available online about these tools is referring to the Ruby implementation, but it’s transferable to the .Net version.
- https://youtu.be/SMadH_ALLII This was a good video (43 mins) on microservices in general, plus testing them using PactNet (recommended viewing)
- http://martinfowler.com/bliki/IntegrationContractTest.html – Theory on this testing approach
- http://dius.com.au/2014/05/19/simplifying-micro-service-testing-with-pacts/ – Some more theory but a good example use case
- http://www.slideshare.net/bethesque/pact-44565612 – Slides working through the ‘why’ and an example use case
- https://github.com/realestate-com-au/pact/wiki – Pact wiki
- https://github.com/realestate-com-au/pact/wiki/Sharing-pacts-between-consumer-and-provider – How to handle dependency between services and avoid stale data
- http://techblog.realestate.com.au/enter-the-pact-matrix-or-how-to-decouple-the-release-cycles-of-your-microservices/ & http://techblog.realestate.com.au/testing-interactions-with-web-services-without-integration-tests-in-ruby/ – Posts from REA on theory of using contract based testing (with PACT)
- http://techblog.newsweaver.com/why-should-you-use-consumer-driven-contracts-for-microservices-integration-tests/ – Another helpful post on the ‘why’ of contract based testing
People that are big contributors to this space worth following or listening to:
- Beth Skurrie – Major contributor and speaker on Pact from REA
- Martin Fowler – Writes a lot on microservices, how to build them and test them, on a theory level, not about particular tools.
- Neil Campbell – Works on the PactNet library
Got any experience testing microservices and lessons to share? Other resources worth including? Please comment below and I’ll include them