Search code examples
node.jsexpressjestjssuperagent

How to mock functions when testing with jest and superagent


I have run into an issue that files and functions cannot be mocked, that is used in a handler of an API call. This call is simulated using superagent.

Here is the code of the test

// users.itest.js
const request = require('superagent');
const get = async url => request
  .get(`${process.env.API_URL}${url}`);

describe('endpoint', () => {
it('GET', async () => {
  jest.mock('../token-store', () => ({
    getToken: jest.fn().mockReturnValue('token'),
  }));

  const { status, body } = await get('/api/users');
  expect(status).toEqual(200);
  expect(body).toHaveValidSchema(userSchema);
});

And here is the handler that is called by the '/api/users' endpoint

const someHandler = async (req, res) => {
  const token = await tokenStore.getToken();

  res.send(token);
};

I tried mocking it like shown, however, I couldn't find a solution. Thanks.


Solution

  • You should use jest.mock() in the module scope, not function scope.

    Here is the integration test solution:

    app.js:

    const express = require('express');
    const tokenStore = require('./token-store');
    
    const app = express();
    
    const someHandler = async (req, res) => {
      const token = await tokenStore.getToken();
      res.send(token);
    };
    
    app.get('/api/users', someHandler);
    
    module.exports = app;
    

    token-store.js:

    async function getToken() {
      return 'real token';
    }
    
    module.exports = {
      getToken,
    };
    

    users.test.js:

    const request = require('superagent');
    const app = require('./app');
    
    const port = 3000;
    process.env.API_URL = `http://localhost:${port}`;
    const get = async (url) => request.get(`${process.env.API_URL}${url}`);
    
    jest.mock('./token-store', () => ({
      getToken: jest.fn().mockReturnValue('token'),
    }));
    
    describe('endpoint', () => {
      let server;
      beforeAll((done) => {
        server = app.listen(port, () => {
          console.info(`HTTP server is listening on http://localhost:${server.address().port}`);
          done();
        });
      });
    
      afterAll((done) => {
        server.close(done);
      });
    
      it('GET', async () => {
        const { status, text } = await get('/api/users');
        expect(status).toEqual(200);
        expect(text).toBe('token');
      });
    });
    

    Integration test result with coverage report:

     PASS  src/stackoverflow/59426030/users.test.js (10.767s)
      endpoint
        ✓ GET (74ms)
    
      console.info src/stackoverflow/59426030/users.test.js:16
        HTTP server is listening on http://localhost:3000
    
    ----------|----------|----------|----------|----------|-------------------|
    File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
    ----------|----------|----------|----------|----------|-------------------|
    All files |      100 |      100 |      100 |      100 |                   |
     app.js   |      100 |      100 |      100 |      100 |                   |
    ----------|----------|----------|----------|----------|-------------------|
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        12.254s
    

    Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59426030