Amy Fang
06/07/2022, 1:30 PMdescribe('Pact test', () => {
it('should work', async () => {
const apiPath = '/userprofile/v1/paymentmethods';
const interaction: InteractionObject = {
state: 'Client has submitted a get all payment methods request',
uponReceiving: 'a valid payment methods get body',
withRequest: {
method: 'GET',
path: '/userprofile/v1/paymentmethods',
query: { userId: 11111 },
},
...
When I go to the Provider contract on Pactflow, it shows that the query param is there (in screenshot)
However, when I run the pact test, it gives me the 1st error Could not find key "userId" in empty Hash at $.query
I've tried multiple variations, such as removing the query property and placing it directly in the path and turning the userId into a string. When I change both types into a string, it gives me the 2nd errorYousaf Nabi (pactflow.io)
Amy Fang
06/07/2022, 1:48 PMYousaf Nabi (pactflow.io)
Amy Fang
06/07/2022, 1:51 PM/* eslint-disable import/no-extraneous-dependencies */
import { pactWith } from 'jest-pact';
import {
InteractionObject, Matchers, Publisher,
} from '@pact-foundation/pact';
import fetch from 'node-fetch';
const opts = {
pactFilesOrDirs: ['pact/pacts'],
pactBroker: '<https://uship.pactflow.io/>',
pactBrokerToken: process.env.PACTFLOW_SECRET_TOKEN,
consumerVersion: process.env.GIT_COMMIT,
publishVerificationResult: true,
branch: process.env.GIT_BRANCH,
tags: ['tag'],
};
pactWith(
{
consumer: 'consumer',
provider: 'provider',
pactfileWriteMode: 'overwrite',
},
async (provider: any) => {
const client = (path, options?) => {
const url = `${provider.mockService.baseUrl}${path}`;
return fetch(url, options);
};
afterAll(async () => {
await new Publisher(opts).publishPacts();
});
describe('Payment methods', () => {
it('should be able to create a pact for get all payment methods', async () => {
const apiPath = '/userprofile/v1/paymentmethods';
const interaction: InteractionObject = {
state: 'Client has submitted a get all payment methods request',
uponReceiving: 'a valid payment methods get body',
withRequest: {
method: 'GET',
path: '/userprofile/v1/paymentmethods',
query = { userId: '111' }
},
willRespondWith: {
headers: {
'Content-Type': 'application/json',
},
body: {
paymentMethods: [{
id: Matchers.like(73),
userId: Matchers.like(44441),
externalId: Matchers.like('id_123'),
type: Matchers.like('card'),
lastFour: Matchers.like('4242'),
isPrimary: Matchers.like(true),
issuer: Matchers.like('Visa'),
verificationStatus: Matchers.like('notapplicable'),
expiration: {
year: Matchers.like(2024),
month: Matchers.like(7),
},
},
{
id: Matchers.like(73),
userId: Matchers.like(44441),
externalId: Matchers.like('id_123'),
type: Matchers.like('payonterms'),
lastFour: null,
isPrimary: Matchers.like(true),
issuer: null,
verificationStatus: Matchers.like('notapplicable'),
creditLimit: {
creditApproved: {
amount: Matchers.like(50000),
currency: Matchers.like('USD'),
},
creditAvailable: {
amount: Matchers.like(38159.03),
currency: Matchers.like('USD'),
},
creditUsed: {
amount: Matchers.like(11840.97),
currency: Matchers.like('USD'),
},
},
},
{
id: Matchers.like(73),
userId: Matchers.like(44441),
externalId: Matchers.like('id_123'),
type: Matchers.like('ACH'),
lastFour: Matchers.like('6789'),
isPrimary: Matchers.like(true),
issuer: Matchers.like('Visa'),
verificationStatus: Matchers.like('notapplicable'),
},
],
},
status: 200,
},
};
await provider.addInteraction(interaction);
const response = await client(apiPath, {
method: 'get',
headers: { 'Content-Type': 'application/json' },
});
expect(response.status).toBe(200);
});
});
},
);
Yousaf Nabi (pactflow.io)
const client = (path, options?) => {
const url = `${provider.mockService.baseUrl}${path}`;
return fetch(url, options);
};
Yousaf Nabi (pactflow.io)
fetch
under the hood
const response = await client(apiPath, {
method: 'get',
headers: { 'Content-Type': 'application/json' },
});
Yousaf Nabi (pactflow.io)
Amy Fang
06/07/2022, 1:55 PMYousaf Nabi (pactflow.io)
Yousaf Nabi (pactflow.io)
Amy Fang
06/07/2022, 1:57 PMYousaf Nabi (pactflow.io)
Yousaf Nabi (pactflow.io)
Yousaf Nabi (pactflow.io)
Yousaf Nabi (pactflow.io)
Amy Fang
06/07/2022, 2:03 PMYousaf Nabi (pactflow.io)
provider
service
const client = (path, options?) => {
const url = `${provider.mockService.baseUrl}${path}`;
return fetch(url, options);
};
when you call that client in your test, it is passed the apiPath
const apiPath = '/userprofile/v1/paymentmethods';
and has no knowledge of the query parameters you have defined in the Pact interaction
const response = await client(apiPath, {
method: 'get',
headers: { 'Content-Type': 'application/json' },
});
If you wanted to pass headers to fetch, there is an example on the fetch website
https://fetch.spec.whatwg.org/#fetch-api
var url = new URL("<https://geo.example.org/api>"),
params = {lat:35.696233, long:139.570431}
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
fetch(url).then(/* … */)
The errors shown by Pact, are saying that the client you have setup is making a request to /userprofile/v1/paymentmethods
but has no query parameters, which is exactly what is being demonstrated by the code shown.
Also additional point to note, as you mentioned a provider contract, I am assuming we are using Bi-Directional contract testing to validate the consumer/provider contracts. https://docs.pactflow.io/docs/bi-directional-contract-testing
If so, the Matchers
used in this pact test, will be persisted to a Pact contract file but will be ignored at verification time. Matching is done based on the type specified in the pact file example https://docs.pactflow.io/docs/bi-directional-contract-testing/compatibility-checks
so this id: Matchers.like(73)
can just be id: 73
and it will expect the field id
to exist and be a number
The matchers are relevant when using consumer driven contract testing, where you are using provider verification driven by the Pact framework https://docs.pact.io/getting_started/how_pact_works#provider-verificationAmy Fang
06/07/2022, 3:32 PMYousaf Nabi (pactflow.io)
Yousaf Nabi (pactflow.io)
Matt (pactflow.io / pact-js / pact-go)
Timothy Jones
06/07/2022, 11:14 PMMatt (pactflow.io / pact-js / pact-go)
Is the query param type on the provider side able to be either string or number, or can it only be string?For clarity, what Tim said