Search code examples
javascriptnode.jsmocha.jsnock

Nock interceptors chaining, second mock ignored


A simple example of a mocking chain of requests with nock.


    const request = require('request-promise');

    module.exports = () => {

      const URL1 = 'https://my.host.com/a/b/c/d';
      const URL2 = 'https://my.host.com/a/b/x/y?k=v';

      const options = {
        method: 'POST',
        uri: URL2,
        body: {
          some: 'payload'
        },
        json: true
      };

      return request(URL1)
        .then(() => request(options))
        .catch(e => console.error(e))
    };


and test for it:


    require('should');
    const nock = require('nock');

    const testFn = require('./');

    describe('Check endpoint requests', () => {

      beforeEach(() => {
        nock.disableNetConnect();
      });

      afterEach(() => {
        nock.cleanAll();
        nock.enableNetConnect();
      });

      it('should hit correct endpoints', () => {
        const scope = nock(`https://my.host.com/a/b`, {
          encodedQueryParams: true,
        })
          .get('/c/d')
          .reply(200)
          .post('/x/y', {
            some: 'payload'
          })
          .query({k: 'v'})
          .reply(200);

        testFn().then(() =>
          scope.isDone().should.be.true()
        );
      });
    });


As a result during the tests, the second "POST" request mock is completely ignored. After the hitting first mock URL1 - nock clearing the pending mocks for that scope and marks it as done.

The thing which counts I thing is that the basic URL is the same.

Is it a bug, or I use it incorrectly.


Solution

  • You have a few minor issues in your test.

    First, the value passed to nock should just be the origin and shouldn't include part of the path. Instead, in your case, get and post should have the full path.

    Second, you want to remove encodedQueryParams: true. That flag means the interceptor is created using already encoded query/search params, however, you're calling it like .query({k: 'v'}), which is not pre-encoded.

    The last issue is that you weren't telling Mocha when the test was finished. So it was completing the test before having all of its results. There are two ways to achieve this. Either accept an argument in the it callback, done is the nomenclature. Or make the callback async and await your requests. I've implemented the latter below.

      it('should hit correct endpoints', async () => {
        const scope = nock('https://my.host.com')
          .get('/a/b/c/d')
          .reply(200)
          .post('/a/b/x/y', {
            some: 'payload'
          })
          .query({k: 'v'})
          .reply(200);
    
        await testFn();
        scope.isDone().should.be.true();
      });