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 specification 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
andarrayContaining
(see example project) - Support for dynamic IDs in requests via provider state injected values (
fromProviderState
matcher) arrayContaining
andeachKeyLike
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
Alpine linux is currently not supported.
- 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 ofoverwrite
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 tomerge
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 pact-foundation/pact-reference#205). However, an array with matchers is supported (see jest spec) which can be used as a workarounnd
- the
mockService
property on thePact
class is no longer an actualMockService
, but supports thebaseUrl
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, thepublishPacts
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.
- N/A - the interface is completely backwards compatible (famous last words! Please let us know if you find this is not the case).
N/A (as per above) - However, you can now validate your metadata too!
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.
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
orwritePact
- 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
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"
}),
});
- The
extractPayload
function on the matchers has been renamedreify
in v3 matchers. The old name (extractPayload
) is still available as an alias, but might be deprecated in the future.