Search code examples
javascripttypescripttestingpact

PACT.io: Getting Missing requests error, when running npm run pactTest


Test Repo created: https://github.com/leongaban/pact-io-js-test

enter image description here

Expected

Run npm run pactTest which will create a Pact file for my TotalPayout.test.pact.ts file.

Results

D, [#38238] 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"
    }
  }
}
W, [#38238]  WARN -- : Verifying - actual interactions do not match expected interactions. 
Missing requests:
  GET /frontoffice/api/liquidity-pool/get-total-payout


W, [#38238]  WARN -- : Missing requests:
  GET /frontoffice/api/liquidity-pool/get-total-payout

Here is my Pact File

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

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

const EXPECTED_BODY = {
  total_payout: 100.21,
};

const userId = 'foo';

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'
          },
          data: 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) => {
          console.log('response', response);
          console.log('EXPECTED_BODY', EXPECTED_BODY);
          expect(response).toEqual(EXPECTED_BODY);
        })
        .then(done);
    });
  });
});

Here is the service file that contains the getTotalPayout function:

This endpoint doesn't exist yet, but this Pact test should still work is my understanding.

// @TODO Note, this is the placeholder for LiquidityPool API endpoints
// @ts-ignore
import axios, * as others from 'axios';

const endpoint = '/frontoffice/api/liquidity-pool/';

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

Also my axios mock in src/__mocks__/axios.ts

// tslint:disable-next-line:no-empty
const mockNoop = () => new Promise(() => {});

export default {
  get: jest.fn(() => Promise.resolve({ data: { total_payout: 100.21 }})),
  default: mockNoop,
  post: mockNoop,
  put: mockNoop,
  delete: mockNoop,
  patch: mockNoop
};

Solution

  • Quite simply - your test is not hitting the path /frontoffice/api/liquidity-pool/get-total-payout on the Pact Mock Server.

    You have setup Pact to run on http://localhost:1234, so your actual code needs to be configured to hit this server, instead of the real one.

    In your axios configuration, you are mocking out the http request library so it does nothing. So when your real code uses it, it doesn't make the http call and your Pact tests fail, because it was expecting a call with a certain shape and didn't get it.

    Here are the things that you need to change:

    1. Axios seems to be used, but is not in your package.json. I’m guessing because you’re mocking it out (2). You need an actual http request library, so you should probably just install axios
    2. Axios is mocked out to provide canned responses, DO NOT mock out axios during Pact testing (it’s ok for local dev if you want) as this will prevent real calls being made to Pact. Pact needs real http calls to it so that it can check your code is doing the right thing, and then write what it found into a 'contract'
    3. Configure axios in tests to hit the Pact mock service. Something like the following should do, assuming you use axios:

    pactSetup.ts:

      // Configure axios to use the Pact mock server for Pact tests
      import axios from "axios";
      axios.defaults.baseURL = "http://localhost:1234";
    
    1. Read the log files that Pact spits out. It tells you what’s going on. To re-iterate: You’ve told Pact that there should be an interaction on /frontoffice/api/liquidity-pool/get-total-payout but it never received it. Make your actual code hit this and then you'll be fine.

    Lastly and as an aside, you might want to use Pact as a local stub server for local development, once you’ve started generating Pacts (after 1-4 are fixed). The binary is actually installed in your node_modules already and the docs for how it operates are at https://github.com/pact-foundation/pact-ruby-standalone/releases

    I typically have a script in package.json that looks something like:

      "stubs": "$(find . -name pact-stub-service | head -n 1) pacts/* --port 4000"
    

    You can then run npm run stubs and have a local stub of the provider running on port 4000, with all of the request/responses you put into your tests.