Hi. I am new to pactflow and contract testing. I h...
# pact-js
t
Hi. I am new to pactflow and contract testing. I have been looking at the example provided at https://killercoda.com/pactflow/scenario/pactflow-getting-started-js But I was wondering how I can extend this to include tests for a product that does not exist i.e. to check that we get a 404 and valid error message. Would I need to modify the client to include a catch block and return an error response e.g. something like ...
async getProduct(id: number) {
return axios
`.get(
${this.url}/products/${id}
)`
.then((r: any) => new Product(r.data.id, r.data.name, r.data.type))
.catch((e: any) => new ErrorResponse(e.data.errorCode, e.data.errorMessage))
}
y
axios throws on non 2xx errors so you need to change your test code to expect it to return with a rejected promise and check the result, assuming you want your code to return the error to whatever code in your app called the api collaborator. if not you can handle the error case in your api client, and do whatever businessey behaviour you want, then use Pact to assert that your api collaborator behaves as you expect it to, when you've got your provider in a state when it will return an error
there will lots of resources on the internet about error handling in axios πŸ‘
t
I'm still pretty confused. So this is my test in my consumer pact spec.
it("product does not exist", async () => {
// set up Pact interactions
const expectedError = { errorCode: '1223', errorMessage: 'Product not found' };
mockProvider
.given("a product with ID 11 does not exist")
.uponReceiving("a request to get a product that doesn't exist")
.withRequest({
method: "GET",
path: "/products/11"
})
.willRespondWith({
status: 404,
body: like(expectedError)
});
return mockProvider.executeTest(async (mockserver) => {
const api = new ProductApiClient(mockserver.url);
const errorResponse = await api.getProduct(11);
expect(errorResponse).toEqual(new ErrorResponse('1223', 'Product not found'));
return;
});
});
But this results in TypeError: Cannot read properties of undefined (reading 'errorCode')
y
I don't know if this helps, https://github.com/pactflow/pact-msw-adapter/pull/94/files#diff-b46ef3cd00c413a1774590ff2e72f13c6e7433bdc5867af7952ce64ef49dccae I came across a different in the error handling in node 20 today, or well on node 20, I got 2 econn refused, only trying localhost the other trying the loopback address
m
I think the issue is that the
e.data
does not exist on the error object. Axios (from googling) returns it as
e.response
https://github.com/axios/axios/issues/376#issuecomment-238034016
So it’s probably just
new ErrorResponse(e.response.errorCode, e.response.errorMessage)
type thing.
but suffice to say yes, you can definitely test 4xx πŸ˜›
t
ok, thanks I've managed to make some progress on this. I found this article 'https://docs.pact.io/implementation_guides/javascript/docs/troubleshooting' which was useful. I changed my consumer spec to be ...
it("product does not exist", async () => {
// set up Pact interactions
const expectedError = { errorCode: 'ERR_BAD_REQUEST', errorMessage: 'Request failed with status code 404' };
mockProvider
.given("a product with ID 11 does not exist")
.uponReceiving("a request to get a product that doesn't exist")
.withRequest({
method: "GET",
path: "/products/11"
})
.willRespondWith({
status: 404,
body: like(expectedError)
});
return mockProvider.executeTest(async (mockserver) => {
const api = new ProductApiClient(mockserver.url);
await expect(api.getProduct2(11)).rejects.toMatch('Request failed with status code 404');
return;
});
});
and my getProduct client function to be ..
async getProduct2(id: number) {
try {
return await axios
`.get(
${this.url}/products/${id}
)`
.then((r: any) => new Product(r.data.id, r.data.name, r.data.type));
} catch (error) {
if (error.errors && error.errors.length > 0) {
return Promise.reject(new Error(error.errors));
} else {
const errorResponse = new ErrorResponse(error.response.data.errorCode, error.response.data.errorMessage )
return Promise.reject(error.response.data.errorMessage)
}
}
}
Above will only work for a single error and just returning the error message. Would like to now see if I can extend it to return the whole error response and validate against that.
I would like to be able to match on the whole error response but when I change my client to ...
catch (error) {
if (error.errors && error.errors.length > 0) {
return Promise.reject(new Error(error.errors));
} else {
const errorResponse = new ErrorResponse(
error.response.data.errorCode,
error.response.data.errorMessage
);
error.response.data.errorMessage));
return Promise.reject(errorResponse);
}
}
but then in my consumer spec test I have....
await expect(api.getProduct2(11)).rejects.toMatch(new ErrorResponse('ERR_BAD_REQUEST', 'Request failed with status code 404'));
This gives me error .... Argument of type 'ErrorResponse' is not assignable to parameter of type 'string | RegExp'.
Found the solutions for this. Used toEqual instead of toMatch so ...
await expect(api.getProduct2(11)).rejects.toEqual(new ErrorResponse('ERR_BAD_REQUEST', 'Request failed with status code 404'));
πŸ‘ 1