Search code examples
node.jsjestjsmockingnock

How to send multiple response using nock if there is no request body


I am calling a streaming api http://dummy-streaming-api/customer-details which accepts post requests and sends some data. It does not require any request body. I want to mock the response using nock in a Node.js application for unit tests. I already have a test case for happy journey in which nock intercepts the request and sends 200 along with some data.

nock('http://dummy-streaming-api')
    .persist()
    .get('/customer-details')
    .reply(200,dummyResponse)

How do I write a nock which sends 500 or 404, as I do not have request body to differentiate between 2 requests.


Solution

  • TLDR;

    I'd suggest removing .persist() and setting up nock in each test with the response you want.

    Details

    The .persist() call makes that particular nock exist for multiple calls in a row. There are a lot of features (eg. sending different replies, or scope.isDone()) that you miss out on by persisting a nock, and persisting opens up to some hard-to-find bugs.

    I'd suggest removing .persist() and instead creating a new nock in each test, and running nock.cleanAll() in your afterEach. This gets you better readability on the current state of nock and what each test is really testing.

    Example:

    afterEach(() => nock.cleanAll())
    
    it('should call the endpoint', async () => {
      const streamApiNock = nock('http://dummy-streaming-api')
        .get('/customer-details')
        .reply(200, dummyResponse)
    
      const response = await myCallToApi(url)
      expect(response.statusCode).to.equal(200)
      expect(streamApiNock.isDone()).to.be.true()
    }
    
    it('should handle an error from the endpoint', async () => {
      const streamApiNock = nock('http://dummy-streaming-api')
        .get('/customer-details')
        .reply(500, {err: 'my error'})
    
      const response = await myCallToApi(url) 
      // this might need a try/catch or an expect().to.reject() or whatever
      // depending on what testing lib you're using
    
      expect(response.statusCode).to.equal(500)
      expect(streamApiNock.isDone()).to.be.true()
    }
    

    If you have multiple tests that require the same nock response and don't want to keep writing the code, I'd put those all in a describe('200 response') block, put the nock call in the beforeEach(), and a nock.cleanAll() in the afterEach(). That way, testing state is obvious in each section of code and gets cleaned up in a way that can prevent hard-to-detect bugs for other developers.

    If you need multiple calls in the same test, like if you were testing retry functionality on your internal fetch function or something, I'd suggest calling nock twice (and using chaining or .times() for cleaner syntax). See docs: Intercepting multiple calls

    Example:

    afterEach(() => nock.cleanAll())
    
    // Sets up nock twice to check for rate limiting/retry functionality
    it('should retry 3 times on 500', async () => {
      nock('http://dummy-streaming-api')
        .get('/customer-details')
        .reply(500, err)
        .get('/customer-details')
        .reply(500, err)
        .get('/customer-details')
        .reply(200, payload)
      
      await myRetryFunction();
    
      expect(nock.isDone()).to.be.true()
    }
    
    it('should retry 3 times on 500', async () => {
      nock('http://dummy-streaming-api')
        .get('/customer-details')
        .times(2)
        .reply(500, err)
        .get('/customer-details')
        .reply(200, payload)
      
      await myRetryFunction();
    
      expect(nock.isDone()).to.be.true()
    }
    

    Chaining: Chaining

    Docs for nock.persist(): Docs