pact-js
JS version of Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.
Top Related Projects
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.
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.
Quick Overview
Pact-js is a JavaScript implementation of the Pact contract testing framework. It allows developers to define and verify consumer-driven contracts between microservices, ensuring that services can communicate effectively with each other. Pact-js supports both Node.js and browser environments, making it versatile for various JavaScript projects.
Pros
- Enables consumer-driven contract testing, improving reliability in microservices architectures
- Supports both Node.js and browser environments
- Integrates well with popular testing frameworks like Jest and Mocha
- Provides a mock server for simulating provider responses during testing
Cons
- Learning curve for developers new to contract testing concepts
- Setup and configuration can be complex for larger projects
- Limited support for certain edge cases in API interactions
- Requires buy-in from both consumer and provider teams for effective implementation
Code Examples
- Defining a Pact interaction:
const interaction = {
state: 'a user exists',
uponReceiving: 'a request for user information',
withRequest: {
method: 'GET',
path: '/api/users/1',
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json' },
body: { id: 1, name: 'John Doe' },
},
};
- Creating a Pact and adding an interaction:
const provider = new Pact({
consumer: 'MyConsumer',
provider: 'MyProvider',
});
await provider.setup();
await provider.addInteraction(interaction);
- Verifying the contract:
try {
// Run your API client code here
const response = await api.getUser(1);
expect(response).toEqual({ id: 1, name: 'John Doe' });
await provider.verify();
} finally {
await provider.finalize();
}
Getting Started
To get started with Pact-js, follow these steps:
-
Install Pact-js in your project:
npm install --save-dev @pact-foundation/pact
-
Create a Pact test file (e.g.,
user.pact.spec.js
):const { Pact } = require('@pact-foundation/pact'); const { api } = require('./api'); describe('User API', () => { const provider = new Pact({ consumer: 'MyConsumer', provider: 'MyProvider', }); beforeAll(() => provider.setup()); afterAll(() => provider.finalize()); it('gets user information', async () => { await provider.addInteraction({ state: 'a user exists', uponReceiving: 'a request for user information', withRequest: { method: 'GET', path: '/api/users/1', }, willRespondWith: { status: 200, body: { id: 1, name: 'John Doe' }, }, }); const response = await api.getUser(1); expect(response).toEqual({ id: 1, name: 'John Doe' }); }); });
-
Run your tests using your preferred test runner (e.g., Jest).
Competitor Comparisons
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.
Pros of pact-ruby
- More mature and established, with a longer history of development and use
- Deeper integration with Ruby ecosystem and testing frameworks
- Supports a wider range of Pact specification versions
Cons of pact-ruby
- Limited to Ruby language and ecosystem
- May have slower performance compared to pact-js for large test suites
Code Comparison
pact-ruby:
Pact.service_consumer "Consumer" do
has_pact_with "Provider" do
mock_service :provider do
port 1234
end
end
end
pact-js:
const provider = new Pact({
consumer: 'Consumer',
provider: 'Provider',
port: 1234
});
Both libraries provide similar functionality for defining Pact contracts, but pact-ruby uses a more Ruby-idiomatic DSL, while pact-js follows a more JavaScript-style object-based approach. The pact-ruby example showcases its deeper integration with Ruby's block syntax, while pact-js demonstrates a more straightforward configuration object.
pact-ruby is ideal for Ruby-based projects with complex testing requirements, while pact-js offers broader language support and may be preferred for JavaScript-heavy or polyglot environments.
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.
Pros of pact-jvm
- Better integration with Java ecosystem and build tools (Maven, Gradle)
- Supports more JVM languages (Java, Kotlin, Scala)
- More mature and feature-rich, with additional plugins and extensions
Cons of pact-jvm
- Steeper learning curve for developers not familiar with JVM languages
- Potentially more complex setup compared to JavaScript-based projects
- Less suitable for frontend-only or Node.js-based applications
Code Comparison
pact-jvm (Java):
@Pact(consumer = "ConsumerName")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("test state")
.uponReceiving("ExampleJavaConsumerPactTest test interaction")
.path("/")
.method("GET")
.willRespondWith()
.status(200)
.body("{\"condition\": true, \"name\": \"tom\"}")
.toPact();
}
pact-js (JavaScript):
const provider = new Pact({
consumer: 'MyConsumer',
provider: 'MyProvider',
});
await provider.addInteraction({
state: 'test state',
uponReceiving: 'a request for something',
withRequest: {
method: 'GET',
path: '/',
},
willRespondWith: {
status: 200,
body: { condition: true, name: 'tom' },
},
});
Both repositories provide similar functionality for contract testing, but pact-jvm is more suitable for JVM-based projects, while pact-js is better for JavaScript and Node.js applications.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Pact JS
Fast, easy and reliable testing for your APIs and microservices.
Pact is the de-facto API contract testing tool. Replace expensive and brittle end-to-end integration tests with fast, reliable and easy to debug unit tests.
Why use Pact? Contract testing with Pact lets you:
Watch our series on the problems with end-to-end integrated tests, and how contract testing can help. |
Documentation
This readme offers a basic introduction to the library. The full documentation for Pact JS and the rest of the framework is available at https://docs.pact.io/.
- Installation
- Consumer Testing
- Provider Testing
- Event Driven Systems
- Plugins
- GraphQL
- XML
- Examples
- Migration guide
- Troubleshooting
Tutorial (60 minutes)
Learn the key Pact JS features in 60 minutes: https://github.com/pact-foundation/pact-workshop-js
Need Help
- Join our community slack workspace.
- Stack Overflow: https://stackoverflow.com/questions/tagged/pact
- Say ð on Twitter: @pact_up
Installation
npm i -S @pact-foundation/pact@latest
# ð now write some tests!
Looking for the previous stable 9.x.x release?
Requirements
Node 16+ as of pact-js v12
- If using pact-js v11 or lower,
- make sure the
ignore-scripts
option is disabled, pact uses npm scripts to compile native dependencies and won't function without it. - Pact uses native extensions and installs them via the
node-gyp
package. This requires a build chain for a successful installation. See also issue #899. This is now prebuilt in pact-js v12+
- make sure the
Do Not Track
In order to get better statistics as to who is using Pact, we have an anonymous tracking event that triggers when Pact installs for the first time. The only things we track are your type of OS, and the version information for the package being installed. No PII data is sent as part of this request. You can disable tracking by setting the environment variable PACT_DO_NOT_TRACK=1
:
Usage
Consumer package
The main consumer interface are the PactV3
class and MatchersV3
exports of the @pact-foundation/pact
package.
Writing a Consumer test
Pact is a consumer-driven contract testing tool, which is a fancy way of saying that the API Consumer
writes a test to set out its assumptions and needs of its API Provider
(s). By unit testing our API client with Pact, it will produce a contract
that we can share to our Provider
to confirm these assumptions and prevent breaking changes.
In this example, we are going to be testing our User API client, responsible for communicating with the UserAPI
over HTTP. It currently has a single method GetUser(id)
that will return a *User
.
Pact tests have a few key properties. We'll demonstrate a common example using the 3A Arrange/Act/Assert
pattern.
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
// Create a 'pact' between the two applications in the integration we are testing
const provider = new PactV3({
dir: path.resolve(process.cwd(), 'pacts'),
consumer: 'MyConsumer',
provider: 'MyProvider',
});
// API Client that will fetch dogs from the Dog API
// This is the target of our Pact test
public getMeDogs = (from: string): AxiosPromise => {
return axios.request({
baseURL: this.url,
params: { from },
headers: { Accept: 'application/json' },
method: 'GET',
url: '/dogs',
});
};
const dogExample = { dog: 1 };
const EXPECTED_BODY = MatchersV3.eachLike(dogExample);
describe('GET /dogs', () => {
it('returns an HTTP 200 and a list of dogs', () => {
// Arrange: Setup our expected interactions
//
// We use Pact to mock out the backend API
provider
.given('I have a list of dogs')
.uponReceiving('a request for all dogs with the builder pattern')
.withRequest({
method: 'GET',
path: '/dogs',
query: { from: 'today' },
headers: { Accept: 'application/json' },
})
.willRespondWith({
status: 200,
headers: { 'Content-Type': 'application/json' },
body: EXPECTED_BODY,
});
return provider.executeTest((mockserver) => {
// Act: test our API client behaves correctly
//
// Note we configure the DogService API client dynamically to
// point to the mock service Pact created for us, instead of
// the real one
dogService = new DogService(mockserver.url);
const response = await dogService.getMeDogs('today')
// Assert: check the result
expect(response.data[0]).to.deep.eq(dogExample);
});
});
});
You can see (and run) the full version of this in ./examples/v3/typescript
, as well as other examples in the parent folder.
To run the examples
- Clone the repo
git@github.com:pact-foundation/pact-js.git
Run a single example
- Change into the required example folder
cd examples/e2e/v3/typescript
- Install all the examples dependencies
npm install
- Run all the example -
npm run test
Run all examples
- Change into the examples folder
cd examples
- Install all the examples dependencies
./installAll.sh
- Run all the examples
./runAll.sh
Provider package
The main provider interface is the Verifier
class of the @pact-foundation/pact
package.
Verifying a Provider
A provider test takes one or more pact files (contracts) as input, and Pact verifies that your provider adheres to the contract. In the simplest case, you can verify a provider as per below using a local pact file, although in practice you would usually use a Pact Broker to manage your contracts and CI/CD workflow.
const { Verifier } = require('@pact-foundation/pact');
// (1) Start provider locally. Be sure to stub out any external dependencies
server.listen(8081, () => {
importData();
console.log('Animal Profile Service listening on http://localhost:8081');
});
// (2) Verify that the provider meets all consumer expectations
describe('Pact Verification', () => {
it('validates the expectations of Matching Service', () => {
let token = 'INVALID TOKEN';
return new Verifier({
providerBaseUrl: 'http://localhost:8081', // <- location of your running provider
pactUrls: [ path.resolve(process.cwd(), "./pacts/SomeConsumer-SomeProvider.json") ],
})
.verifyProvider()
.then(() => {
console.log('Pact Verification Complete!');
});
});
});
It's best to run Pact verification tests as part of your unit testing suite, so you can readily access stubbing, IaC and other helpful tools.
Compatibility
Specification Compatibility
Version | Stable | Spec Compatibility | Install |
---|---|---|---|
11.x.x + | Yes | 2, 3, 4 | See installation |
10.x.x | Yes | 2, 3, 4 | See installation |
9.x.x | Yes | 2, 3* | 9xx |
* v3 support is limited to the subset of functionality required to enable language inter-operable Message support.
Supported Platforms
OS | Architecture | Supported | Pact-JS Version |
---|---|---|---|
MacOS | x86_64 | â | All |
MacOS | arm64 | â | 9.x + |
Linux (libc) | x86_64 | â | All |
Linux (libc) | arm64 | â | 10.x + |
Linux (musl) | x86_64 | â | 13.x + |
Linux (musl) | arm64 | â | 13.x + |
Windows | x86_64 | â | All |
Windows | x86 | â | 9.x - |
Windows | arm64 | â | - |
Roadmap
The roadmap for Pact and Pact JS is outlined on our main website.
Contributing
See CONTRIBUTING.
Top Related Projects
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.
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.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot