Skip to content

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 link
  • PACT_BROKER_PORT - the port to use to connect to Pact Broker. For HTTPS should be 443
  • PACT_BROKER_SCHEME - the HTTP protocol to use. Should be https
  • PACT_BROKER_USERNAME - the username to use for authentication
  • PACT_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