Hi all, I’ve previously written pact tests using a...
# pact-js
a
Hi all, I’ve previously written pact tests using axios as the http handler but the consumer app itself uses fetch (from node-fetch) instead. I’ve tried to switch to using fetch but hit problems. I understand that using jest-pact is a possible solution so I’ve implemented the following example:
Copy code
import { URL } from 'url';

import { Matchers } from '@pact-foundation/pact';
import { HTTPMethod } from '@pact-foundation/pact/src/common/request';
import { pactWith } from 'jest-pact';
import fetch from 'node-fetch';

jest.setTimeout(30000);

pactWith({ consumer: 'aggregator', provider: 'theme_provider' }, (provider) => {
    describe('Theme related end points', () => {
        it('Request to save themes', async () => {
            const url = new URL(`${provider.mockService.baseUrl}/themes-service/themes`);

            const { validateExample } = Matchers;

            const interaction = {
                state: 'Request to save themes',
                uponReceiving: 'Request to save themes',
                withRequest: {
                    method: <http://HTTPMethod.POST|HTTPMethod.POST>,
                    path: url.pathname,

                    headers: { Accept: 'application/json' },
                },
                willRespondWith: {
                    status: 200,
                    headers: { 'Content-Type': 'application/json' },
                    body: {
                        results: [{ handle: 'abc', result: 'Created' }],
                    },
                },
            };

            await provider.addInteraction(interaction);
            const result = await fetch(`${provider.mockService.baseUrl}/themes-service/themes`, {
                method: <http://HTTPMethod.POST|HTTPMethod.POST>,
                headers: {
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                },
            });

            expect(await result.json()).toEqual({
                results: [
                    {
                        handle: 'abc',
                        result: 'Created',
                    },
                ],
            });
        });
    });
});
Unfortunately, I get the following error:
Copy code
FAIL  __pact__/fetch2.pact.spec.ts (10.793 s)
  Pact between aggregator and theme_provider
    with 30000 ms timeout for Pact
      Theme related end points
        ✕ Request to save themes (59 ms)

  ● Pact between aggregator and theme_provider › with 30000 ms timeout for Pact › Theme related end points › Request to save themes

    FetchError: invalid json response body at  reason: Unexpected end of JSON input

      42 |             });
      43 |
    > 44 |             expect(await result.json()).toEqual({
         |                    ^
      45 |                 results: [
      46 |                     {
      47 |                         handle: 'abc',

      at node_modules/cross-fetch/node_modules/node-fetch/lib/index.js:273:32
      at Object.<anonymous> (__pact__/fetch2.pact.spec.ts:44:20)
If I remove
await
from the offending line of code then the test fails because the response promise never gets fulfilled 😕
Copy code
FAIL  __pact__/fetch2.pact.spec.ts (12.368 s)
  Pact between aggregator and theme_provider
    with 30000 ms timeout for Pact
      Theme related end points
        ✕ Request to save themes (65 ms)

  ● Pact between aggregator and theme_provider › with 30000 ms timeout for Pact › Theme related end points › Request to save themes

    expect(received).toEqual(expected) // deep equality

    - Expected  - 8
    + Received  + 1

    - Object {
    -   "results": Array [
    -     Object {
    -       "handle": "abc",
    -       "result": "Created",
    -     },
    -   ],
    - }
    + Promise {}

      42 |             });
      43 |
    > 44 |             expect(result.json()).toEqual({
         |                                   ^
      45 |                 results: [
      46 |                     {
      47 |                         handle: 'abc',

      at Object.<anonymous> (__pact__/fetch2.pact.spec.ts:44:35)
What am I missing or overlooking? I'd be really grateful for any assistance on this problem 🙂
m
What do the mock server logs say they received?
It's possible cors is the issue
a
Hi Matt, not sure where to find the logs for the mock server.
Is that the
pact.log
file?
I've added the following to the provider setup
Copy code
log: path.resolve(process.cwd(), '__pact__/logs', 'pact-fetch.log'),
        logLevel: 'debug',
        cors: true,
....and the output looks like this
Copy code
FAIL  __pact__/fetch2.pact.spec.ts (10.086 s)
  Pact between aggregator and theme_provider
    with 30000 ms timeout for Pact
      Theme related end points
        ✕ Request to save themes (84 ms)

  ● Pact between aggregator and theme_provider › with 30000 ms timeout for Pact › Theme related end points › Request to save themes

    expect(received).toEqual(expected) // deep equality

    - Expected  - 8
    + Received  + 1

    - Object {
    -   "results": Array [
    -     Object {
    -       "handle": "abc",
    -       "result": "Created",
    -     },
    -   ],
    - }
    + Promise {}

      51 |                 });
      52 |
    > 53 |                 expect(result.json()).toEqual({
         |                                       ^
      54 |                     results: [
      55 |                         {
      56 |                             handle: 'abc',

      at Object.<anonymous> (__pact__/fetch2.pact.spec.ts:53:39)
....and the
pact.log
file looks like this
Copy code
I, [2022-04-01T11:43:11.829104 #62695]  INFO -- : Registered expected interaction POST /themes-service/themes
D, [2022-04-01T11:43:11.829281 #62695] DEBUG -- : {
  "description": "Request to save themes",
  "providerState": "Request to save themes",
  "request": {
    "method": "POST",
    "path": "/themes-service/themes",
    "headers": {
      "Accept": "application/json"
    }
  },
  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "application/json"
    },
    "body": {
      "results": [
        {
          "handle": "abc",
          "result": "Created"
        }
      ]
    }
  },
  "metadata": null
}
W, [2022-04-01T11:43:11.844186 #62695]  WARN -- : Verifying - actual interactions do not match expected interactions. 
Missing requests:
	POST /themes-service/themes



W, [2022-04-01T11:43:11.844236 #62695]  WARN -- : Missing requests:
	POST /themes-service/themes



I, [2022-04-01T11:43:11.895725 #62695]  INFO -- : Cleared interactions
I've located the mock server log aggregator-theme_provider-mockserver-interaction.log and it has the following
Copy code
W, [2022-04-01T11:03:36.800180 #58467]  WARN -- : Verifying - actual interactions do not match expected interactions. 
Missing requests:
	POST /themes-service/themes



W, [2022-04-01T11:03:36.800294 #58467]  WARN -- : Missing requests:
	POST /themes-service/themes
I've set
cors
to both true and false with no effect 😕
.
@Matt (pactflow.io / pact-js / pact-go) is it possible that the json response data isn't being extracted from the promise correctly?
m
That error is pretty clear - the fetch call didn't send the request to the expected path. My guess is it's something to do with running fetching in a node process
a
Hi @Matt (pactflow.io / pact-js / pact-go), is this a known issue between pact-js and fetch? Any other advice on how to proceed with this issue?
m
I'm not aware of any issues
If you could pls create a repro we can take a look
a
Hi @Matt (pactflow.io / pact-js / pact-go) Looks like we got to the bottom of our problem with using fetch and pact-js.
It turns out that, in the unit tests, we're using jest-fetch-mock so our pact tests were calling a mocked fetch to begin with.
In our pact tests we've added the following lines to disable fetchMock:
Copy code
import fetchMock from 'jest-fetch-mock';
...
...

beforeAll(async () => {
    fetchMock.disableMocks();
});

afterAll(async () => {
    fetchMock.enableMocks();
});
m
Ha, yes that'll do it 🤣
😁 1