“Captain! All the integration tests have failed again! Could it be just another false negative?”
“Well, you just never know, Son. Fire all the torpedoes anyway”
I feel the way we do integration testing is sort of like driving your car from Melbourne to Sydney each time your mechanic makes a change to it. Or setting your house on fire to test your smoke alarm. It is excessive, tiresome and way too costly.
In a previous blog I talked about using the Pact gem to validate integration points in your architecture (see Testing interactions with web services without integration tests in ruby).
Since then Pact has improved a lot, and the original example was a bit contrived. Well, totally contrived. I thought it time to provide a more concrete example using the latest version of Pact.
What is Pact?
From the Pact website:
Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.
Essentially, Pact provides a mechanism for creating a contract between a service consumer and a service provider, and then providing the tools to validate that the consumer and provider adhere to the contact independently of each other.
An example: Splitting out events to an Event Service
For one of our clients, we were required to create a RESTful Web API for customers to get a feed of events that have occurred in their web application. In this particular case, the web application was extremely large and complex, as we did not want to add more to it. We decided to create a new small service in Ruby that would house the API, and we would push events to it via HTTP as they occurred.
The source system would do a POST request to an event resource URI, and this would create a new event in the event store. This new event would then be available via a feed to users of the API. The API would use a HMAC authentication scheme, and the events would be sent and stored in JSON format. For the Ruby HMAC library I used, refer to https://github.com/Asquera/warden-hmac-authentication.
Testing the interactions between the source system and the event API
The source system is a large web application that requires a lot of running components to function. And our new web API required a data store to store the events. We sure didn’t want to create a test environment with all of the services running in it just to make sure we haven’t broken the interaction between the two applications.
For this particular interaction, the consumer is the source system which is going to push the events to the event API. It will use a HTTP Client library (HTTParty in this case) to make the request and include all the necessary headers for HMAC authentication scheme. Here is a very simple version that posts a JSON version of an event and returns a boolean.
The event model looks something like this:
This event generates the following JSON (running on my laptop):
The actual request that gets sent is:
We will start with the Consumer and create a Pact test which will define the contract between the source system and the RESTful web API. We’ll use the Pact DSL which integrates nicely into rspec. First we create a pact helper:
When the spec runs, this will start a mock HTTP server listening on port 1234. The spec file follows:
Running this test passes, and it generates a pact file of the interaction.
Our little Ruby web service is going to provide an event resource that can accept POST requests containing the events. It will validate the request and authenticate it using a HMAC scheme. We’re using Grape for the API and Warden HMAC gem for the HMAC verification . We will also use the ROAR gem for converting the JSON into an Event model.
We can now verify the pact file generated in our consumer test against our provider. Pact has a very convenient rake task to do this (see the section in Pact service provider project in the pact documentation). We just need to setup a helper in spec/service_consumers/pact_helper.rb to conﬁgure the veriﬁcation task with the path to the pact ﬁle.
Running the pact verify task gives us two errors:
The second error is easy to fix. In our client we don’t actual expect a response body, only the success (200) HTTP code. So expecting a content type header is incorrect. We can fix that in our consumer pact test by removing the header. Also, a POST to a Grape API returns a 201, not a 200. So we should change that too.
The first error is due to the missing authentication headers, so we got an unauthenticated (401) response. We’ll need to add those to our consumer spec.
Getting the Pact tests to pass
I’ll update the client to expect a 201 instead of a 200.
We just need to get the HMAC Authentication headers added to our consumer pact test. Our client class already calculates these headers, so they will be easy to add. Here is the updated spec:
Running this generates the following pact file:
Now, coping this latest pact over to the provider project and re-running the verification task, we get a success, Yay!
Instead of having to build a test environment running with two servers and databases to test that our interaction between our source system and our event API works, we now have two fast tests that achieve the same result. As long as we maintain the pact file between the two systems, we can be reasonably sure that our integration is working correctly.
There is a number of ways of maintaining the pact file. We can get our consumer build to publish the pact file to the CI server as a build artefact. The pact verification task supports loading the pact file from a URL. That way we can get it to always use the pact file from the latest successful build.
There is also a Pact Broker project that provides a repository of pact files. It allows you to be able to verify your providers against pact files from different versions of your consumer, statically check the compatibility of consumers and providers, and automate your CI and CD workflows.
NOTE: The Pact Broker is an open source tool that requires you to deploy, administer and host it yourself. If you would prefer a plug-and-play option, we’ve created Pactflow, a fully managed Pact Broker with additional features to simplify teams getting started and scaling with Pact and contract testing. You can get started quickly and for free on our Developer Plan