I am using pact V3 in typescript to verify a contract with an API. The response example matcher is the following:
const userExample = {
email: string("foo.bar@example.com"),
id: number(123)
}
The actual response.data
from the API looks like:
{
"email": "foo.bar@example.com",
"id": 123
}
When I execute the test:
expect(response.data).toStrictEqual(userExample)
I get that it is comparing the response.data
to the Matcher object instead of the actual example values:
contract:test: - Expected - 8
contract:test: + Received + 2
contract:test:
contract:test: Object {
contract:test: - "email": Object {
contract:test: - "pact:matcher:type": "type",
contract:test: - "value": "foo.bar@example.com",
contract:test: - },
contract:test: - "id": Object {
contract:test: - "pact:matcher:type": "number",
contract:test: - "value": 123,
contract:test: - },
contract:test: + "email": "foo.bar@example.com",
contract:test: + "id": 123,
contract:test: }
I can get around this by instead doing:
expect(response.data.email).toStrictEqual(userExample.email.value)
expect(response.data.id).toStrictEqual(userExample.id.value)
But I am trying to figure out if there is a way to compare the entire response without having to specify the .value
in a separate expect
function line for each potential value of the userExample
response?
Full test code below:
import { MatchersV3, PactV3 } from "@pact-foundation/pact"
const { like, atLeastOneLike, string, number } = MatchersV3
const provider = new PactV3({
consumer: "consumer-service",
provider: "provider-service"
})
const userExample = {
email: string("foo.bar@example.com"),
id: number(123),
}
test("get users", async () => {
provider.addInteraction({
states: [{ description: "user exists" }],
uponReceiving: "get users",
withRequest: {
method: "GET",
path: "/users",
},
willRespondWith: {
status: 200,
body: like({
users: atLeastOneLike(userExample),
}),
},
})
await provider.executeTest(async (mockserver) => {
const userServiceClient = new UserServiceClient(mockserver.url)
const response = await userServiceClient.getUsers()
expect(response.data.email).toStrictEqual(userExample.email.value)
expect(response.data.id).toStrictEqual(userExample.id.value)
})
})
You can use the reify
function to do this (previously called extractPayload
), to remove the matches from the object.
This being said:
const userExample = {
email: string("foo.bar@example.com"),
id: number(123),
}
is equivalent to
const userExample = like({
email: "foo.bar@example.com",
id: 123,
})
That is, a like
matcher just matches on types.
atLeastOneLike
is also an extension of this matcher, but for arrays of objects.
So you could rewrite your test as follows:
const userExample = {
email: "foo.bar@example.com",
id: 123,
}
test("get users", async () => {
provider.addInteraction({
states: [{ description: "user exists" }],
uponReceiving: "get users",
withRequest: {
method: "GET",
path: "/users",
},
willRespondWith: {
status: 200,
body: like({
users: atLeastOneLike(userExample),
}),
},
})
await provider.executeTest(async (mockserver) => {
const userServiceClient = new UserServiceClient(mockserver.url)
const response = await userServiceClient.getUsers()
expect(response.data.email).toStrictEqual(userExample.email)
expect(response.data.id).toStrictEqual(userExample.id)
})
})