If you’ve been ready the parts I’ve written so far, you’ll know we’ve gone through what contract testing is, the pact framework and the consumer side code for a specific scenario.
In this part, we’ll be examining the provider side code of the same scenario. I’d therefore recommend checking out the earlier parts if you haven’t yet, or even for a refresher as this part may not have much context otherwise.
As before I’ll be using Java for this example, but there are many other supported languages and Pact provides excellent documentation and examples: https://docs.pact.io/implementation_guides.
Scenario
Let’s remind ourselves of the scenario we are using for this pact:
Consumer
Our consumer is a simple API that orchestrates data from a number of services and consumes from the Employee Service.
Provider
The provider is a REST service that covers Create/Read/Update/Delete (CRUD) operations for the Employee object.
Pact
The pact for this example is:
Given An employee exists with id of 1
When I request to view that employee
Then I am returned success and a single employee object
Provider Code
Now that we have refreshed ourselves on the scenario, let’s take a look at the code. The provider side only has the responsibility of running the tests against itself, so the test code is fairly simple compared to the consumer.
Dependancies
You need to add the pact framework for the provider for your chosen technology. For Java, the following package is needed:
<dependency>
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-provider-spring_2.11</artifactId>
<version>3.5.23</version>
<scope>test</scope>
</dependency>
This was the latest version at the time of writing, but I’d use the latest stable one when you come to use it.
Provider test class
Let’s create a simple boilerplate class for running the pacts:
@RunWith(SpringRestPactRunner.class)
@Provider("employee-service")
@PactFolder("pacts")
@SpringBootTest(
classes = EmployeeServiceApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class EmployeeServicePactProviderTestIT {
@TestTarget
public final Target target = new SpringBootHttpTarget();
}
Let’s example the annotations and fields:
- We are using SpringRestPactRunner to run this test as the app is a Java Spring Boot app and this works well in this case, other Java runners (like simply PactRunner) are running and what you need will depend on your application under test
- Provider – just refers to the provider so we only run pacts for this provider, must match the name used in the pact
- PactFolder – tells the test where the pact files are (in this case a local resource folder in the project called “pacts”)
- SpringBootTest – part of the spring boot framework and signifies this is an integration test which requires the application to be running when executing
- TestTarget – part of the JUnit test runner and tells the test where to hit when running the tests – we give it a SpringBootHttpTarget, which is an addition to the pact framework and tells it to use the running spring boot app as the target
Just having this test class will cause the pact framework to be run as part of our build tests. This means when we build and run tests, pact will look in the given folder and attempt to run those pacts it finds. Currently it will find our pact and if it runs, it will error with the following:
- MissingStateMethodException
This error means that the provider has run our pact and the pact is saying it needs a particular state but we haven’t provided a method to create that state.
State Method
So let’s create our method now. All we want to do in this method is use the pact framework to tie in the method to the given state and then set the provider up in that state.
@State("An employee exists with id of 1")
public void anEmployeeExistsWithAnIdOfOne() throws SQLException, IOException {
//mock the service layer
}
There is nothing to it! The reason for this is I haven’t included the mock code because that will be completely different for any application. Although for most applications, mocking a service is extremely simple. The important bit to note is the @State annotation which is the part of the pact framework that ties this method to the given state in the pact from our consumer.
Mocking
Why are we mocking? Well, we are mocking at the layer below the contract because we are not testing this in the contract tests. All we want to test is the contract itself and not the functionality of the provider. Therefore we allow the controller to do it’s work as this is the input point for the consumer and we mock below that as changing this won’t affect the contract. It might affect the returned data values, but we can verify that using unit and integration tests.
Request Validation
There may be occasions when you want to do validation on the incoming request itself – simple data and field checks. If mocking, you have to ensure that this validation is not mocked – this is because it’s a part of the contract. The incoming request structure is a vital part of any consumer contract as it’s the consumer telling you what it will send you.
Summary
So we’ve explored how to create provider code to run your contracts you’ve been given by the consumer and we can now successfully pass our contract tests!
However the approach we have is a bit clunky due to passing files around and not having it in our CI system. In part 5, we’ll explore the pact broker and integrating pact in to a CI system.