Node.JS
Node.JS

Reputation: 1570

jest + supertest: how to reset the mocked dependency

I am not able to reset jest mocks of a dependency after it's being used once by supertest. I appreciate any help or hint.

The following is my api test with supertest:

import request from 'supertest';
import router from '../index';
const app = require('express')();
app.use(router);

jest.mock('config', () => ({}));

jest.mock('express-request-proxy', () => data => (req, res, next) =>
  res.json(data),
);

beforeEach(() => {
  jest.resetAllMocks();
});

describe('GET /', () => {
  it('should get all stuff', () =>
    request(app)
      .get('')
      .expect(200)
      .expect('Content-Type', /json/)
      .then(response => {
        expect(response.body).toMatchSnapshot();             // all good here 
      }));

  it('should get all stuff when FOO=bar', async () => {
    jest.mock('config', () => ({
      FOO: 'bar',
      get: key => key,
    }));

    // >> HERE <<
    // config.FOO is still undefined!
    // reseting mocks did not work ...
    await request(app)
      .get('')
      .expect(200)
      .expect('Content-Type', /json/)
      .then(response => {
        expect(response.body.query).toHaveProperty('baz');   // test fails ...
      });
  });
});

express.js api:

const router = require('express').Router({ mergeParams: true });
import config from 'config';
import proxy from 'express-request-proxy';

router.get('', (...args) => {
  let query = {};
  if (config.FOO === 'bar') {
    query.baz = true;
  }
  return proxy({
    url: '/stuff',
    query,
  })(...args);
});

Upvotes: 3

Views: 2488

Answers (1)

Lin Du
Lin Du

Reputation: 102207

You can't use jest.mock(moduleName, factory, options) in a function scope, it should be used in the module scope. You should use jest.doMock(moduleName, factory, options) if you want to arrange the mocks in function scope of test cases.

We also need to use jest.resetModules() before executing each test case to

reset the module registry - the cache of all required modules.

It means your ./config module registry will be reset so that it will return different mocked values for each test case when you require it after jest.doMock('./config', () => {...}) statement.

{ virtual: true } option means I don't install the express-request-proxy package so it doesn't exist in my npm_modules directory. If you had already installed it, you can remove this option.

Here is the unit test solution:

index.js:

const router = require('express').Router({ mergeParams: true });

import config from './config';
import proxy from 'express-request-proxy';

router.get('', (...args) => {
  console.log(config);
  let query = {};
  if (config.FOO === 'bar') {
    query.baz = true;
  }
  return proxy({ url: '/stuff', query })(...args);
});

export default router;

config.js:

export default {
  FOO: '',
};

index.test.js:

import request from 'supertest';

jest.mock('express-request-proxy', () => (data) => (req, res, next) => res.json(data), { virtual: true });

beforeEach(() => {
  jest.resetAllMocks();
  jest.resetModules();
});

describe('GET /', () => {
  it('should get all stuff', () => {
    jest.doMock('./config', () => ({}));
    const router = require('./index').default;
    const app = require('express')();
    app.use(router);

    return request(app)
      .get('')
      .expect(200)
      .expect('Content-Type', /json/)
      .then((response) => {
        expect(response.body).toMatchSnapshot();
      });
  });

  it('should get all stuff when FOO=bar', async () => {
    jest.doMock('./config', () => ({
      default: {
        FOO: 'bar',
        get: (key) => key,
      },
      __esModule: true,
    }));
    const router = require('./index').default;
    const app = require('express')();
    app.use(router);

    await request(app)
      .get('')
      .expect(200)
      .expect('Content-Type', /json/)
      .then((response) => {
        expect(response.body.query).toHaveProperty('baz');
      });
  });
});

unit test results with 100% coverage report:

 PASS  stackoverflow/61828748/index.test.js (11.966s)
  GET /
    ✓ should get all stuff (8710ms)
    ✓ should get all stuff when FOO=bar (24ms)

  console.log
    {}

      at stackoverflow/61828748/index.js:7:11

  console.log
    { FOO: 'bar', get: [Function: get] }

      at stackoverflow/61828748/index.js:7:11

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   1 passed, 1 total
Time:        13.268s

index.test.js.snap:

// Jest Snapshot v1

exports[`GET / should get all stuff 1`] = `
Object {
  "query": Object {},
  "url": "/stuff",
}
`;

source code: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/61828748

Upvotes: 2

Related Questions