Skip to main content

Migrating from 9.x.x to 10.x.x

Major changes

It's fast - very fast! The Ruby shared core has been replaced by the Rust shared core. This does use node-gyp under the hood, to enable native dependencies to be compiled for the platform at install time.

  • Ability to create and verify both v2 and v3 pacts
  • Support for all v3 matchers and generators, and other v3 items (such as multiple provider states with parameters)
  • XML content-type support (via XmlBuilder), Binary and MIME/multipart body matching
  • Improved support for Hypermedia APIs such as HAL and Siren through url and arrayContaining (see example project)
  • Support for dynamic IDs in requests via provider state injected values (fromProviderState matcher)
  • arrayContaining and eachKeyLike matchers for improved array matching
  • Ability to modify request bodies and headers during verification (via requestFilter on the provider)
  • Ability to specify and verify message metadata
  • Each test is given a dedicated server and port, simplifying lifecycle events and improving isolation between tests, also allowing the possibility of parallel tests if needed (but, you probably don't)
  • ... and many other bug fixes and improvements

Breaking changes

Alpine linux is currently not supported.

Consumer

  • The undocumented Monkey patching support no longer exists (there is no Ruby to monkeypatch!)
  • Removed public json() method from consumer message pact
  • pactfileWriteMode still supports the same options, however the behaviour of overwrite is such now that it will overwrite the pact file per test, not pact run of Pact. This is because there is no longer a single long running mock server as per previous versions. Set to merge or leave blank for a sensible default.
  • As per the change to pactfileWriteMode this also means pact files should be cleared out prior to each test, to avoid extraneous interactions in a pact file.
  • Array matcher currently doesn't work on query strings (see https://github.com/pact-foundation/pact-reference/issues/205). However, an array with matchers is supported (see jest spec) which can be used as a workarounnd
  • the mockService property on the Pact class is no longer an actual MockService, but supports the baseUrl property for compatibility.
  • Manually controlling the state of the mock server - such as removing interactions - has been removed.
  • There is currently no support for specifying custom tls certificates in consumer Pact tests
  • Publisher has been removed in favour of encouraging usage of the CLI. If you require a JS API to do this, the publishPacts operation still exists in https://github.com/pact-foundation/pact-js-core/#pact-broker-publishing.
  • PactWeb has been removed entirely and is no longer supported.

Provider

  • N/A - the interface is completely backwards compatible (famous last words! Please let us know if you find this is not the case).

Messages

N/A (as per above) - However, you can now validate your metadata too!

Basic migration

Consumer

Pact -> Pact

Most users should be able to upgrade the package using existing imports and things will just work™️, however you should take note of the breaking behaviour changes above.

Pact -> PactV3

If you would like to upgrade to the latest package to take advantage of new matchers, generators etc., you'll need to make some minor changes to how you setup your tests.

The main differences:

  • There is no longer any need to setup lifecycle methods for things like setup, finalize or writePact - they are done automatically for you.
  • You now need to wrap your assertions in an executeTest call.

Given the following test:

describe("API Pact test", () => {
beforeAll(() => provider.setup());
afterEach(() => provider.verify());
afterAll(() => provider.finalize());

describe("getting all products", () => {
test("products exists", async () => {

// set up Pact interactions
await provider.addInteraction({
states: [{description: 'products exist'}],
uponReceiving: 'get all products',
withRequest: {
method: 'GET',
path: '/products'
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: eachLike({
id: "09",
type: "CREDIT_CARD",
name: "Gem Visa"
}),
},
});

const api = new API(provider.mockService.baseUrl);

// make request to Pact mock server
const product = await api.getAllProducts();

expect(product).toStrictEqual([
{"id": "09", "name": "Gem Visa", "type": "CREDIT_CARD"}
]);

});
});
});

you would re-write it as follows:

describe("API Pact test", () => {
describe("getting all products", () => {
test("products exists", async () => {

// set up Pact interactions
await provider.addInteraction({
states: [{description: 'products exist'}],
uponReceiving: 'get all products',
withRequest: {
method: 'GET',
path: '/products'
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: eachLike({
id: "09",
type: "CREDIT_CARD",
name: "Gem Visa"
}),
},
});

// Need to wrap the call in execute test. The method accepts a function
// that provides the details of the dynamic mock server
await provider.executeTest(async (mockService) => {
const api = new API(mockService.url);

// make request to Pact mock server
const product = await api.getAllProducts();

expect(product).toStrictEqual([
{"id": "09", "name": "Gem Visa", "type": "CREDIT_CARD"}
]);
})

});
});
});

If you were using the builder interface:

const interaction = new Interaction()
.given('products exist')
.uponReceiving('a request to get all products')
.withRequest({
method: 'GET',
path: '/products',
headers: {
Accept: 'application/json',
},
})
.willRespondWith({
status: 200,
headers: {
'Content-Type': 'application/json',
},
body: eachLike({
id: "09",
type: "CREDIT_CARD",
name: "Gem Visa"
}),
});

return provider.addInteraction(interaction);

You can now simply do this:

provider
.given('products exist')
.uponReceiving('a request to get all products')
.withRequest({
method: 'GET',
path: '/products',
})
.willRespondWith({
status: 200,
headers: { 'content-type': 'application/json' },
body: eachLike({
id: "09",
type: "CREDIT_CARD",
name: "Gem Visa"
}),
});

Other

  • The extractPayload function on the matchers has been renamed reify in v3 matchers. The old name (extractPayload) is still available as an alias, but might be deprecated in the future.