Testing microservices – Learn. Write. Repeat.
Now the fun part. Assuming that we have built a running microservice environment, how can we maintain it? How do we introduce changes without breaking it? How do we test it? We know we can do end to end tests, but these are slow and we need faster feedback. How can we test the microservice in isolation?
One solution that gains traction is Consumer Driven Contract Testing (CDC-T). Let's examine the words that build this structure.
We need to make sure that our service works as expected, and more importantly can be deployed without breaking the dependent services. CDC-T is a form of unit testing if you consider your service a unit. But since the service is composed of multiple modules it is integration testing, as you test that the modules that compose the service integrates with each other. Since a unit means the smallest component possible, that means CDC-T is a form of integration testing. We all know testing something means using test doubles. When using test doubles we need to make sure they are not out of sync with the actual service/client that consumes them. This leads us to the next term.
A contract is a set of rules that must be followed. For example an interface is a contract. It exposes a set of operation signatures that an implementation must follow. A contract can contains also the type of communications. From the service point of view, it refers to its rest api (if we are talking about rest services). This api is being used by one or more consumers. This is the contract that the service as a provider is offering.
Because the clients are the consumers. They are deciding what its important, and their requirements must be met. The consumers must decide what the contract should contain, and the service must follow that without breaking other consumers. A service provider can be consumed by one or more clients. That means a service can be bound to multiple consumer contracts.
Putting these together, CDC-T is a way of making sure that the contracts specified by the clients are not broken by writing tests using contract stubs. By stubbing these contracts we can test the client without the presence of a service, therefore achieving quick feedback. One huge advantage of this is that the services are driven by the business needs. In a way CDC-T is a form of TDD applied on a different level.
Spring contract implements this pattern. On the consumer side Spring Cloud Contract generates stubs, which you can use during client-side testing. You get a running WireMock instance/Messaging route that simulates the service. You would like to feed that instance with a proper stub definition.
On the service side you need to be sure that the stub it actually resembles your concrete implementation. You cannot have a situation where your stub acts in one way and your application behaves in a different way, especially in production. To ensure that your application behaves the way you define in your stub, tests are generated from the stub you provide.
What the above contract is basically saying is that the consumer expects a service that is able to return the item with the code A. And for this request the server will return some fake data.
The + sign means that it will resolve to the latest version.
When running this test the consumer service will start and along with it the stubbed provider service. The test will pass without the presence of real provider service, as long as WireMock request mappings contains the request which is being made on the provider service. Those mappings are determined from the contracts.
At this point I recommend to use the exchange method of the RestTemplate since it allows you to control headers and avoid WireMock stubbed request not matching the real one.
That's it. The producer creates and publishes stubs which will be consumed by the clients using a maven repository as a central repo.