Consumer-Driven Contract Testing with Pact for Angular and Spring Boot
Setting up Contract Testing using Pact, Angular with Karma and Jest and Spring Boot with JUnit 5
When setting up Contract testing for our project we had some issues getting Pact working nicely with Angular Testbed and Karma/Jest. The code examples that are available are either outdated or simply not working when having multiple providers.
In this article I will go step by step showing how to setup a Consumer-Driven Contract Testing framework with Pact. It will include an Angular frontend as a Consumer and a Spring Boot backend as a Provider.
Code Example
I have created an example repository on GitHub containing all the code:
Getting started
Create a new directory which will be the root for our Consumers, Providers and contracts. We will use both Typescript/Javascript and Java for this guide.
Creating the Consumer
Since the framework is consumer-driven we will start with our first consumer, the Angular frontend. We will both use Karma and Jest to run the Pact tests.
Angular CLI
To create the first Angular app we will use Angular CLI. First create a directory consumers
from the root dir and in this directory run:
ng new angular-karma
Keep every setting default and the Angular app will be created
As you can see above we will create an Angular app per testrunner to keep things clear. We will start with Karma.
Adding the service
To have something to contract test we will add a User service to our Angular project. Go to the src/app
folder and create the user.service.ts
file. It will be a simple service for our User endpoint:
Setting up Pact using Karma
Install the following packages:
npm install --save-dev @pact-foundation/karma-pact @pact-foundation/pact-node @pact-foundation/pact-web
At this time there is still an issue with the
@pact-foundation/karma-pact
plugin not working with Karma 4, so we need to lower the Karma version to^3.1.4
in thepackage.json
file.
Edit the karma.conf.js
file in the root of the Angular project:
The important things to note about pact
:
- the used specification version
- which port the Pact mock service will be running
- where the contracts will be written. Since we will not be using a Pact broker for this guide, we will use a shared directory in the root folder to share contract between Consumers and Providers.
Also note the proxies
part that makes sure our calls get proxied to the Pact mock server.
Now run ng test
/npm run test
/ yarn test
to start the Karma testrunner. It will start the Pact mock server:
INFO: pact-node@10.2.2/9882 on hades.local:
Creating Pact Server with options:
{"cors":true,"spec":3,"port":1234,"log":"<logdir>","dir":"<pactdir>","pactFileWriteMode":"overwrite","ssl":false,"host":"localhost"}
INFO [pact]: Pact Mock Server running on port: 1234
Since we don’t have any Contract tests yet this is all Pact will do for now
Adding Consumer Contract Test
To integrate closely with Angular we will write our Contract tests next to the service. To separate it from unit tests we will call it user.service.pact.spec.ts
To write our contract test we first need to setup the Pact framework. To use Pact with Karma we need to use PactWeb
instead of the Pact
Node.js library. In the beforeAll
we add a new PactWeb
configuration for this service:
This configuration determines which Consumer/Provider pair is contract tested. The port number is for the port on which the Pact mock server should be available.
We will use Angular TestBed to setup our service for testing:
Next are the callbacks for verifying the mock service and writing the contract file:
With all this plumbing we can create our first contract test:
In the beforeAll
we setup the mock service to accept a GET request on the /api/users/1
path and return an example user we defined above. The test will then call our Angular service and validate the expected response is returned.
When we now run ng test
a contract file is generated in the root pacts
folder. If you view this file you can see it contains our definition of the contract as defined by our test code for this Consumer/Provider pair:
Setting up Pact using Jest
When using Jest instead of Karma there are some setup changes, but the contract tests stay the same.
Create a new Angular app from the root dir using ng new angular-jest
.
Add the same user.service.ts
as shown for the Karma framework.
Then install the following packages:
npm install --save-dev @angular-builders/jest @pact-foundation/pact @pact-foundation/pact-node @types/jest jest jest-preset-angular
Add the jest.pact.config.js
file in the root of the Angular project:
Things to note in this config are the testUrl
param, which makes sure all our calls will be directed to our Pact mock server. Also we have a setupFile that will setup our Jest framework. It’s located in pact/jest/setupJest.ts
:
To make sure Jest will work we need to edit the tsconfig.spec.json
and add the emitDecoratorMetadata
and experimentalDecorators
tags. This prevent errors with the jest-preset-angular
package as stated here:
The final file to edit will be angular.json
. Default Angular uses Karma instead of Jest for testing. Find the test
part in the json and change it to:
Things to note here are the runInBand
parameter to make sure Jest will run the Pact tests sequentially instead of parallel.
Adding Consumer Contract Test
This step is mostly the same as for our Karma setup, with a few minor changes. We will be using the Pact
Node.js library instead. First setup the Pact mock server:
The other steps are the same as for Karma:
After this the contract test is exactly the same as in the Karma framework:
Now we can run the contract test using ng test
.
Creating the Provider
Since we now have our Consumers and their contracts we can create the Provider. We will use Spring Boot for that.
Setup Spring Boot
I used the Spring Initializr site to create the base for the Provider. Create a Maven project using Java. Fill in the required fields and then extract it to the providers/spring-boot
folder.
You can run the backend service using mvn clean spring-boot:run
Adding User Service
We will add a new RestController to handle our request for the User Service:
We also will need a model for User:
Setting up Pact using JUnit 5
To setup our contract verification we will use the JUnit 5 Pact extension.
First add the following packages to your pom file:
To make use of our contracts in the root folder we include them in our testResources
so we can acces them from the Java classpath:
Setting up the contract verification is now very simple:
Things to note:
@Provider
annotation refers to the Provider name, it has to match the Provider name used in the Consumer test. It will only test contracts that match the Provider name.@PactFolder
refers to the folder the test can find the contracts.- The
start
will start our Spring Boot application. - In the
before
method we point Pact to the correct base url where the Provider is available. - The
@State
annotation provides a method to setup our backend to reach that state. In our case we don’t need do to anything. - Finally in the
pactVerificationTestTemplate
method the contracts are verified.
Afterthoughts
In this guide we created an Angular frontend and Spring Boot backend. We then proceeded to create a contract from the Angular frontend and verified that it abides the contract. We then verified the contract for the Spring boot backend.
In this guide we haven’t looked at Spring Cloud Contract, which is another framework to setup contract testing for Spring Boot applications. But it is not mutually exclusive since Spring Cloud Contract can convert contracts to Pact versions.