Local Setup¶
Pact Broker CLI¶
Install Pact Broker CLI
gem install pact-broker-client
pact-broker version
pact-broker -h
Java¶
directory structure
Similar to functional tests and performance tests, contract tests should be separated out from unit tests. This allows them to be ran separately.
├── build.gradle
├── settings.gradle
├── src
│ ├── contractTest
│ ├── functionalTest
│ ├── main
│ ├── performanceTest
│ └── test
build.gradle
You will need to update the sourceSets
to tell gradle where the contractTest source files live.
sourceSets
sourceSets {
contractTest {
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
java {
}
}
}
Provider¶
Include the Pact dependencies for the contractTestImplementation
dependencies block
contractTestImplementation 'org.jetbrains.kotlin:kotlin-stdlib:1.6.0'
contractTestImplementation 'au.com.dius.pact.provider:junit5:4.3.2'
Add the Gradle task for executing the contract tests. Here we are also setting a set of system properties for publishing verification results. The system properties here are read from a set of environment variables.
The environment variables are:
PACT_PROVIDER_VERSION
- the version of the provider. This should be either a Git commit hash or a Git tag (e.g. $CIRCLE_SHA1 or $CIRCLE_TAG)PACT_BROKER_URL
- the URL for pact broker. Should be pact-broker.clientcloud.com if the client already has pact broker, update this link. If not, we can reference the generic pact broker linkPACT_BROKER_PORT
- the port to use to connect to Pact Broker. For HTTPS should be 443PACT_BROKER_SCHEME
- the HTTP protocol to use. Should be httpsPACT_BROKER_USERNAME
- the username to use for authenticationPACT_BROKER_PASSWORD
- the password to use for authentication
Set the environment variables
export PACT_BROKER_URL=pact-broker.clientcloud.com [if the client already has pact broker, update this link. If not, we can reference the generic pact broker link](liatrio-tag)
export PACT_BROKER_PORT=443
export PACT_BROKER_SCHEME=https
export PACT_BROKER_USERNAME=<NEED TO BE ONBOARDED TO PACT BROKER>
export PACT_BROKER_PASSWORD=<NEED TO BE ONBOARDED TO PACT BROKER>
Gradle Task
task contractTest(type: Test) {
description = "Run contract tests"
group = "verification"
testClassesDirs = sourceSets.contractTest.output.classesDirs
classpath = sourceSets.contractTest.runtimeClasspath
useJUnitPlatform()
systemProperty "spring.profiles.active", "local"
systemProperty "pact.provider.version", System.env.PACT_PROVIDER_VERSION ?: "0.0.0"
systemProperty 'pactbroker.host', System.env.PACT_BROKER_URL ?: "localhost"
systemProperty 'pactbroker.port', System.env.PACT_BROKER_PORT ?: "9292"
systemProperty 'pactbroker.scheme', System.env.PACT_BROKER_SCHEME ?: 'http'
systemProperty 'pactbroker.auth.username', System.env.PACT_BROKER_USERNAME ?: ""
systemProperty 'pactbroker.auth.password', System.env.PACT_BROKER_PASSWORD ?: ""
systemProperty 'pact.verifier.publishResults', true
}
Then to execute the contract tests
./gradlew clean contractTest
Consumer¶
For the consumer, there is a Gradle plugin that can be included in order to publish the Pact>
plugins block
id "au.com.dius.pact" version "4.1.0"
Include the Pact dependencies for the contractTestImplementation
dependencies block
contractTestImplementation 'org.jetbrains.kotlin:kotlin-stdlib:1.6.0-RC2'
contractTestImplementation 'au.com.dius.pact.consumer:junit5:4.2.14'
Add the Gradle task for executing the contract tests. Here we add two separate tasks; One for publishing the pact, and another for executing the contract tests.
Gradle Tasks
task contractTest(type: Test) {
description = "Run contract tests"
group = "verification"
testClassesDirs = sourceSets.contractTest.output.classesDirs
classpath = sourceSets.contractTest.runtimeClasspath
useJUnitPlatform()
}
pact {
publish {
pactBrokerUrl = 'https://pact-broker.clientcloud.com' [if the client already has pact broker, update this link. If not, we can reference the generic pact broker link](liatrio-tag)
pactBrokerUsername = System.env.PACT_BROKER_USERNAME
pactBrokerPassword = System.env.PACT_BROKER_PASSWORD
}
}
Then to execute the contract tests
./gradlew clean contractTest
To publish the pact
./gradlew pactPublish
Node¶
directory structure
Here, the contract tests are nested under another folder called test
. The contract test folder will be a nested project with its own package.json and jest configuration.
├── jest.config.js
├── jest.setup.js
├── package.json
├── public
├── src
├── test
│ ├── contract
│ │ ├── jest.config.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── publish.js
│ │ └── tests
test/contract/package.json
Update the dependencies block to include the necessary dependencies.
dependencies block
"@pact-foundation/absolute-version": "^0.0.3",
"@pact-foundation/pact": "^9.15.5",
"jest": "^27.0.6",
"jest-pact": "^0.9.1",
The publish.js file will have the script for publishing the generated pact to Pact Broker.
test/contract/publish.js
import { Publisher } from "@pact-foundation/pact";
import { versionFromGitTag } from "@pact-foundation/absolute-version";
import path from "path";
const branch = process.env.CIRCLE_BRANCH;
const consumerVersion = versionFromGitTag();
const pactBrokerUrl = process.env.PACT_BROKER_URL || "http://localhost:9292";
const pactBrokerUsr = process.env.PACT_BROKER_USERNAME || "";
const pactBrokerPwd = process.env.PACT_BROKER_PASSWORD || "";
const opts = {
pactFilesOrDirs: [path.resolve("contracts")],
pactBroker: pactBrokerUrl,
pactBrokerUsername: pactBrokerUsr,
pactBrokerPassword: pactBrokerPwd,
tags: [branch],
consumerVersion,
};
new Publisher(opts)
.publishPacts()
.then(() => {
console.log(
`Pact contract for consumer version ${consumerVersion} published!`
);
})
.catch((e) => {
console.log("Pact contract publishing failed: ", e);
});
scripts block in test/contract/package.json
"publish": "node publish.js"
package.json
scripts block in package.json
"testContract": "npm run test --workspace test/contract"
Then to execute the contract tests from the top level folder
npm install
npm run testContract
To publish the pact
npm run publish --workspace test/contract