• Stars
    star
    2,160
  • Rank 21,289 (Top 0.5 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 11 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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.

Pact

Test Backers on Open Collective Sponsors on Open Collective

Define a pact between service consumers and providers, enabling "consumer driven contract" testing.

Pact provides a fluent API for service consumers to define the HTTP requests they will make to a service provider and the HTTP responses they expect back. These expectations are used in the consumer specs to provide a mock service provider. The interactions are recorded, and played back in the service provider specs to ensure the service provider actually does provide the response the consumer expects.

This allows testing of both sides of an integration point using fast unit tests.

This gem is inspired by the concept of "Consumer driven contracts". See this article by Ian Robinson for more information.

What is it good for?

Pact is most valuable for designing and testing integrations where you (or your team/organisation/partner organisation) control the development of both the consumer and the provider, and the requirements of the consumer are going to be used to drive the features of the provider. It is fantastic tool for developing and testing intra-organisation microservices.

What is it not good for?

  • Testing new or existing providers where the functionality is not being driven by the needs of the consumer (eg. public APIs)
  • Testing providers where the consumer and provider teams do not have good communication channels.
  • Performance and load testing.
  • Functional testing of the provider - that is what the provider's own tests should do. Pact is about checking the contents and format of requests and responses.
  • Situations where you cannot load data into the provider without using the API that you're actually testing (eg. public APIs). Why?
  • Testing "pass through" APIs, where the provider merely passes on the request contents to a downstream service without validating them. Why?

Features

  • A service is mocked using an actual process running on a specified port, so javascript clients can be tested as easily as backend clients.
  • "Provider states" (similar to fixtures) allow the same request to be made with a different expected response.
  • Consumers specify only the fields they are interested in, allowing a provider to return more fields without breaking the pact. This allows a provider to have a different pact with a different consumer, and know which fields each cares about in a given response.
  • RSpec and Minitest support for the service consumer codebase.
  • Rake tasks allow pacts to be verified against a service provider codebase.
  • Different versions of a consumer/provider pairs can be easily tested against each other, allowing confidence when deploying new versions of each (see the pact_broker and pact_broker-client gems).
  • Autogenerated API documentation - need we say more?
  • Autogenerated network diagrams with the Pact Broker

How does it work?

  1. In the specs for the provider facing code in the consumer project, expectations are set up on a mock service provider.
  2. When the specs are run, the mock service returns the expected responses. The requests, and their expected responses, are then written to a "pact" file.
  3. The requests in the pact file are later replayed against the provider, and the actual responses are checked to make sure they match the expected responses.

Pact explanation diagram

Why is developing and testing with Pact better than using traditional system integration tests?

  • Faster execution.
  • Reliable responses from mock service reduce likelihood of flakey tests.
  • Causes of failure are easier to identify as only one component is being tested at a time.
  • Design of service provider is improved by considering first how the data is actually going to be used, rather than how it is most easily retrieved and serialised.
  • No separate integration environment required for automated integration tests - pact tests run in standalone CI builds.
  • Integration flows that would traditionally require running multiple services at the same time can be broken down and each integration point tested separately.

Getting help

Installation

Add this line to your application's Gemfile:

gem 'pact'
# gem 'pact-consumer-minitest' for minitest

And then execute:

$ bundle

Or install it yourself as:

$ gem install pact

Usage - an example scenario

We're going to write an integration, with Pact tests, between a consumer, the Zoo App, and its provider, the Animal Service. In the Consumer project, we're going to need a model (the Alligator class) to represent the data returned from the Animal Service, and a client (the AnimalServiceClient) which will be responsible for making the HTTP calls to the Animal Service.

Example

In the Zoo App (consumer) project

1. Start with your model

Imagine a model class that looks something like this. The attributes for an Alligator live on a remote server, and will need to be retrieved by an HTTP call to the Animal Service.

class Alligator
  attr_reader :name

  def initialize name
    @name = name
  end

  def == other
    other.is_a?(Alligator) && other.name == name
  end
end

2. Create a skeleton Animal Service client class

Imagine an Animal Service client class that looks something like this.

require 'httparty'

class AnimalServiceClient
  include HTTParty
  base_uri 'http://animal-service.com'

  def get_alligator
    # Yet to be implemented because we're doing Test First Development...
  end
end

3. Configure the mock Animal Service

The following code will create a mock service on localhost:1234 which will respond to your application's queries over HTTP as if it were the real "Animal Service" app. It also creates a mock provider object which you will use to set up your expectations. The method name to access the mock service provider will be what ever name you give as the service argument - in this case "animal_service"

# In /spec/service_providers/pact_helper.rb

require 'pact/consumer/rspec'
# or require 'pact/consumer/minitest' if you are using Minitest

Pact.service_consumer "Zoo App" do
  has_pact_with "Animal Service" do
    mock_service :animal_service do
      port 1234
      host "..." # optional, defaults to "localhost"
    end
  end
end

4. Write a failing spec for the Animal Service client

# In /spec/service_providers/animal_service_client_spec.rb

# When using RSpec, use the metadata `:pact => true` to include all the pact functionality in your spec.
# When using Minitest, include Pact::Consumer::Minitest in your spec.

describe AnimalServiceClient, :pact => true do

  before do
    # Configure your client to point to the stub service on localhost using the port you have specified
    AnimalServiceClient.base_uri 'localhost:1234'
  end

  subject { AnimalServiceClient.new }

  describe "get_alligator" do

    before do
      animal_service.given("an alligator exists").
        upon_receiving("a request for an alligator").
        with(method: :get, path: '/alligator', query: '').
        will_respond_with(
          status: 200,
          headers: {'Content-Type' => 'application/json'},
          body: {name: 'Betty'} )
    end

    it "returns an alligator" do
      expect(subject.get_alligator).to eq(Alligator.new('Betty'))
    end

  end

end

5. Run the specs

Running the AnimalServiceClient spec will generate a pact file in the configured pact dir (spec/pacts by default). Logs will be output to the configured log dir (log by default) that can be useful when diagnosing problems.

Of course, the above specs will fail because the Animal Service client method is not implemented, so next, implement your provider client methods.

6. Implement the Animal Service client consumer methods

class AnimalServiceClient
  include HTTParty
  base_uri 'http://animal-service.com'

  def get_alligator
    name = JSON.parse(self.class.get("/alligator").body)['name']
    Alligator.new(name)
  end
end

7. Run the specs again.

Green! You now have a pact file that can be used to verify your expectations of the Animal Service provider project.

Now, rinse and repeat for other likely status codes that may be returned. For example, consider how you want your client to respond to a:

  • 404 (return null, or raise an error?)
  • 500 (specifying that the response body should contain an error message, and ensuring that your client logs that error message will make your life much easier when things go wrong)
  • 401/403 if there is authorisation.

In the Animal Service (provider) project

1. Create the skeleton API classes

Create your API class using the framework of your choice (the Pact authors have a preference for Webmachine and Roar) - leave the methods unimplemented, we're doing Test First Develoment, remember?

2. Tell your provider that it needs to honour the pact file you made earlier

Require "pact/tasks" in your Rakefile.

# In Rakefile
require 'pact/tasks'

Create a pact_helper.rb in your service provider project. The recommended place is spec/service_consumers/pact_helper.rb.

See Verifying Pacts and the Provider section of the Configuration documentation for more information.

# In spec/service_consumers/pact_helper.rb

require 'pact/provider/rspec'

Pact.service_provider "Animal Service" do

  honours_pact_with 'Zoo App' do

    # This example points to a local file, however, on a real project with a continuous
    # integration box, you would use a [Pact Broker](https://github.com/pact-foundation/pact_broker) or publish your pacts as artifacts,
    # and point the pact_uri to the pact published by the last successful build.

    pact_uri '../zoo-app/spec/pacts/zoo_app-animal_service.json'
  end
end

3. Run your failing specs

$ rake pact:verify

Congratulations! You now have a failing spec to develop against.

At this stage, you'll want to be able to run your specs one at a time while you implement each feature. At the bottom of the failed pact:verify output you will see the commands to rerun each failed interaction individually. A command to run just one interaction will look like this:

$ rake pact:verify PACT_DESCRIPTION="a request for an alligator" PACT_PROVIDER_STATE="an alligator exists"

4. Implement enough to make your first interaction spec pass

Rinse and repeat.

5. Keep going til you're green

Yay! Your Animal Service provider now honours the pact it has with your Zoo App consumer. You can now have confidence that your consumer and provider will play nicely together.

Using provider states

Each interaction in a pact is verified in isolation, with no context maintained from the previous interactions. So how do you test a request that requires data to already exist on the provider? Read about provider states here.

Configuration

See the Configuration section of the documentation for options relating to thing like logging, diff formatting, and documentation generation.

Pact best practices

As in all things, there are good ways to implement Pacts, and there are not so good ways. There are also some Pact GOTCHAS to beware of! Check out the Best practices section of the documentation to make sure you're not Pacting it Wrong.

Current Pact specification version

Currently, Ruby Pact supports writing Pacts in v2, and verifying Pacts in v3 format, HOWEVER it only supports the rules that were defined in v2 (like and term). If you are interested in helping add support for the v3 rules, please talk to @Beth in the #pact-ruby channel on our Slack.

Docs

Related libraries

Pact Provider Proxy - Verify a pact against a running server, allowing you to use pacts with a provider of any language.

Pact Broker - A pact repository. Provides endpoints to access published pacts, meaning you don't need to use messy CI URLs in your codebase. Enables cross testing of prod/head versions of your consumer and provider, allowing you to determine whether the head version of one is compatible with the production version of the other. Helps you to answer that ever so important question, "can I deploy without breaking all the things?"

Pact Broker Client - Contains rake tasks for publishing pacts to the pact_broker.

Shokkenki - Another Consumer Driven Contract gem written by one of Pact's original authors, Brent Snook. Shokkenki allows matchers to be composed using jsonpath expressions and allows auto-generation of mock response values based on regular expressions.

A list of Pact implementations in other languages - JVM, .Net, Javascript and Swift

Links

Simplifying microservices testing with pacts - Ron Holshausen (one of the original pact authors)

Pact specification

Integrated tests are a scam - J.B. Rainsberger

Consumer Driven Contracts - Ian Robinson

Integration Contract Tests - Martin Fowler

Roadmap

See ROADMAP.md.

Contributing

See CONTRIBUTING.md.

Contributors

This project exists thanks to all the people who contribute. [Contribute].

Backers

Thank you to all our backers! πŸ™ [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

More Repositories

1

pact-js

JS version of Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.
TypeScript
1,456
star
2

pact-jvm

JVM version of Pact. 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.
Kotlin
1,077
star
3

pact-go

Golang version of Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.
Go
856
star
4

pact-net

.NET version of Pact. 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.
C#
826
star
5

pact_broker

Enables your consumer driven contracts workflow
Ruby
702
star
6

pact-python

Python version of Pact. 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.
Python
501
star
7

pact-specification

Describes the pact format and verification specifications
295
star
8

pact-php

PHP version of Pact. 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
PHP
244
star
9

pact.io

Pact Foundation Website
HTML
219
star
10

pact-js-core

Core binaries for pact-js, a Contract Testing Framework. NOTE: If you are looking to do Pact contract testing in node, you almost certainly want pact-js, not pact-node.
TypeScript
146
star
11

pact-workshop-js

Pact JS workshop - learn Pact in 60 minutes
JavaScript
129
star
12

pact-workshop-go

Golang Pact workshop
Go
106
star
13

pact-broker-docker

Dockerized Pact Broker
Shell
92
star
14

pact-reference

Reference implementations for the pact specifications
Rust
87
star
15

pact-workshop-jvm-spring

Example Spring Boot project for the Pact workshop
Java
85
star
16

jest-pact

A Pact adaptor for to allow you to easily run tests with Jest
TypeScript
80
star
17

pact-workshop-dotnet-core-v1

A workshop for Pact using .NET Core
C#
76
star
18

pact-stub-server

Standalone pact stub server
Rust
75
star
19

pact-mock_service

Provides a mock service for use with Pact
Ruby
73
star
20

pact_broker-client

A Ruby and CLI client for the Pact Broker. Publish and retrieve pacts and verification results.
Ruby
69
star
21

nestjs-pact

Injectable Pact.js Consumer/Producer for NestJS
TypeScript
48
star
22

devrel

Developer Relations @ Pact - Your map to the Pact landscape for all-comers (maintainers, contributors, users, newbies)
38
star
23

pact-ruby-standalone

A standalone pact command line executable using the ruby pact implementation and Travelling Ruby
Shell
35
star
24

pact-provider-verifier

Cross-platform, generic language, Pact provider verification tool
Ruby
28
star
25

pact-workshop-Maven-Springboot-JUnit5

Pact Maven + Springboot + JUnit5 workshop - learn Pact in 60 minutes
28
star
26

pact-stub-server-archived

Wraps the Pact Rust mock server in a Docker container
Dockerfile
25
star
27

pact-js-mocha

EXPERIMENTAL Mocha Interface for Pact JS (warning: it is not recommended for beginners)
JavaScript
18
star
28

pact-plugins

🏰 Architecture to support Plugins πŸ”Œ with Pact πŸ”—
Rust
16
star
29

pact-provider-proxy

Allows pact verification against a running provider at a configurable base URL
Ruby
16
star
30

docs.pact.io

Pact documentation website
HTML
15
star
31

pact-broker-chart

This repository houses the Pact Broker Helm Chart
Smarty
11
star
32

karma-pact

Pact Framework Plugin for Karma
Shell
11
star
33

pact-5-minute-getting-started-guide

JavaScript
11
star
34

pact-ruby-cli

Amalgamated Pact Ruby CLI
Shell
11
star
35

pact-support

Shared code for Pact gems
Ruby
7
star
36

pact_broker-serverless

Pact Broker running in AWS Lambda with Serverless
Shell
7
star
37

pact-cplusplus

C++ DSL for Pact Library
HTML
6
star
38

pact-ruby-e2e-example

Code base to use for demonstrating features and recreating issues in the ruby implementation of pact. Please fork it and modify to recreate your own code.
Ruby
5
star
39

pact-xml

XML support for the Pact gem
Ruby
4
star
40

pact-plugin-template-golang

Pact πŸ”— Plugin πŸ”Œ template for the GoLang 🐿️ language = 🫢
Go
4
star
41

pact-message-demo

Ruby
4
star
42

pact-consumer-minitest

Minitest support for the Pact Consumer gem
Ruby
4
star
43

serverless-offline-pact

A serverless offline plugin to start one or more pact stub service alongside your serverless application
TypeScript
3
star
44

pact-mock-service-docker

Docker image running the Pact mock service
Ruby
3
star
45

homebrew-pact-ruby-standalone

The Pact Ruby Standalone public homebrew tap for macos/linux homebrew formulae
Shell
3
star
46

.github

The GitHub landing page for Pact - The de-facto contract testing tool
3
star
47

pact-event-bot

TypeScript
2
star
48

pact-standalone-npm

Pact Standalone wrapper for NPM projects
JavaScript
2
star
49

pact-parser

Small server to aid using existing pacts files as data sources in unit and integrational testing.
JavaScript
2
star
50

pact-mock-service-npm

Shell
2
star
51

pact-js-cli

The Broker CLI for Pact, but available to your node scripts
TypeScript
2
star
52

grunt-pact

A grunt task to run pact-node
JavaScript
1
star
53

pactr

R version of Pact. Enables consumer driven contract testing. Please read the Pact.io for specific information about PACT.
R
1
star
54

release-gem

Github action that bumps the version, generates the changelog, releases the gem, and creates a Github release
Shell
1
star
55

mocha-pact

An adaptor to allow you to easily run pact tests with Mocha
TypeScript
1
star
56

pact-ruby-standalone-e2e-example

Code base to use for demonstrating features and recreating issues in the Ruby standalone implementation of pact. Please fork it and modify to recreate your own code.
Shell
1
star
57

cypress-pact

Pact plugin for integrating Pact with Cypress tests
1
star
58

pact-js-dev-config

Shared configs for developers working on pact-js and related projects
JavaScript
1
star
59

blog.pact.io

Ghost application setup for blog.pact.io
SCSS
1
star