'Error with Typescript Pact.io test: PopsicleError: Unable to connect to

Expected: Running npm run pactTest should generate a pact file (JSON).

Results: I get an Unable to connect error.

Pact.io JavaScript implementation guide.

Pact.io Typescript test example.

Appreciate any thoughts or ideas as to what I'm doing wrong :)


The Error

FAIL src/services/api/TotalPayout.test.pact.ts The API getUsersTotalPayout ✕ Should call getUsersTotalPayout and return an object with the total_payout (45ms)

● The API › getUsersTotalPayout › Should call getUsersTotalPayout and return an object with the total_payout

PopsicleError: Unable to connect to "http://127.0.0.1:12345/interactions" Caused by: Error: connect ECONNREFUSED 127.0.0.1:12345

 at Request.Object.<anonymous>.Request.error (node_modules/popsicle/src/request.ts:91:12)
  at ClientRequest.<anonymous> (node_modules/popsicle/src/index.ts:218:31)

package.json script:

"pactTest": "export NODE_ENV=pactTest && jest --testRegex \"/*(.test.pact.ts)\" --runInBand --setupFiles ./pactSetup.ts --setupTestFrameworkScriptFile ./pactTestWrapper.ts",

My src/pactSetup.ts file

// @ts-ignore
import path from 'path';
import { Pact } from '@pact-foundation/pact/pact';
​
// @ts-ignore
global.provider = new Pact({
  port: 1234,
  log: path.resolve(process.cwd(), 'logs', 'mockserver-integration.log'),
  dir: path.resolve(process.cwd(), 'pacts'),
  spec: 2,
  cors: true,
  pactfileWriteMode: 'update',
  consumer: 'Exchange',
  provider: 'LP Service'
});

My src/pactTestWrapper.ts

jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; // This is to give the pact mock server time to start
​
// @ts-ignore
beforeAll(() => provider.setup()); // Create mock provider
// @ts-ignore
afterEach(() => provider.verify()); // Ensure the mock provider verifies expected interactions for each test
// @ts-ignore
afterAll(() => provider.finalize()); // Tear down the mock and write the pact

The test: src/services/api/TotalPayout.test.pact.ts

// @ts-ignore
import path from 'path';
import { Pact } from '@pact-foundation/pact';
import { getTotalPayout } from './apiPayout';

const port = 12345;
const endpoint = '/frontoffice/api/get-total-payout';

const EXPECTED_BODY = {
  total_payout: 100.21,
};

const userId = 'foo';

const provider = new Pact({
  port,
  log: path.resolve(process.cwd(), 'logs', 'mockserver-integration.log'),
  dir: path.resolve(process.cwd(), 'pacts'),
  spec: 2,
  consumer: 'Exchange',
  provider: 'LP Service',
  pactfileWriteMode: 'merge'
});
​​
describe('The API', () => {
  // Copy this block once per interaction under test
  describe('getUsersTotalPayout', () => {
    beforeEach(() => {
      const interaction = {
        uponReceiving: 'a GET request with a user id',
        withRequest: {
          method: 'GET',
          path: endpoint,
          headers: {
            Accept: 'application/json',
          },
        },
        willRespondWith: {
          status: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: EXPECTED_BODY
        },
      };

      // @ts-ignore
      return provider.addInteraction(interaction);
    });
​
    // add expectations
    it('Should call getUsersTotalPayout and return an object with the total_payout', done => {
      getTotalPayout(userId)
        .then((response: any) => {
          expect(response).toEqual(EXPECTED_BODY);
        })
        .then(done);
    });
  });
});

The api service file apiPayout.ts

// @ts-ignore
import axios, * as others from 'axios';

const endpoint = '/frontoffice/api/';

export const getTotalPayout = async (userId: string) => {
  const response = await axios.get(`${endpoint}get-total-payout`, { params: userId });
  return response.data;
};

From the mockserver-integration.log

I, [2018-09-19T11:07:41.259437 #79922]  INFO -- : Verifying - interactions matched
I, [2018-09-19T11:07:41.264440 #79922]  INFO -- : Cleared interactions

From the debug-log

20 error code ELIFECYCLE
21 error errno 1
22 error [email protected] pactTest: `export NODE_ENV=pactTest && jest --testRegex "/*(.test.pact.ts)" --runInBand --setupFiles ./pactSetup.ts --setupTestFrameworkScriptFile ./pactTestWrapper.ts`
22 error Exit status 1
23 error Failed at the [email protected] pactTest script.

Update

After commenting out the provider setup logic in the test.pact file and re-running npm run pactTest I get the following:

console.error node_modules/@pact-foundation/pact/pact.js:110 Pact verification failed!

console.error node_modules/@pact-foundation/pact/pact.js:111 Actual interactions do not match expected interactions for mock MockService.

Missing requests: GET /frontoffice/api/liquidity-pool/get-total-payout

See /Users/leongaban/projects/trade.io/tradeio-front/logs/mockserver-integration.log for details.

enter image description here

And my updated mockserver-intergration.log

I, [2018-09-19T14:12:19.128823 #82330]  INFO -- : Registered expected interaction GET /frontoffice/api/liquidity-pool/get-total-payout
D, [2018-09-19T14:12:19.129127 #82330] DEBUG -- : {
  "description": "a GET request with a user id",
  "request": {
    "method": "GET",
    "path": "/frontoffice/api/liquidity-pool/get-total-payout",
    "headers": {
      "Accept": "application/json"
    }
  },
  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "application/json"
    },
    "body": {
      "total_payout": 100.21
    }
  }
}
W, [2018-09-19T14:12:19.139198 #82330]  WARN -- : Verifying - actual interactions do not match expected interactions. 
Missing requests:
    GET /frontoffice/api/liquidity-pool/get-total-payout


W, [2018-09-19T14:12:19.139254 #82330]  WARN -- : Missing requests:
    GET /frontoffice/api/liquidity-pool/get-total-payout


Solution 1:[1]

Several issues I can point out:

  • You seem to be declaring and spinning a pact mock server twice: in the src/pactSetup.ts file and also in TotalPayout.test.pact.ts which I'm not sure it's what you intended to do. You probably want to avoid declaring the provider in the TotalPayout test, and instead you already have the provider object on the global scope as part of the test framework setup files.

  • In the code apiPayout.ts you are referring to the endpoint URL, but to which port is it sending the request? This API call should be ultimately caught by the pact mock provider that you are spinning up. If you call to a different port than what the mock provider is listening on you'll never hit it.

  • A small nitpick: /frontoffice/api/get-total-payout is not a RESTful. You want to avoid including verbs such as "get" in your API and use the proper HTTP method for that (GET).

Solution 2:[2]

Not sure if that was your problem, but I had the order of writePact and finalize wrong. I had:

afterAll(async () => {
  await provider.finalize()
  await provider.writePact()
})

instead of the correct order:

afterAll(async () => {
  await provider.writePact()
  await provider.finalize()
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 chiyanc22
Solution 2 Arnold Graf