gremo
gremo

Reputation: 48909

In Jest, how do I mock an external module ONLY in a specific test?

I'm using Jest with TypeScript (using ts-jest). This is my test subject which uses dotenv library:

import * as dotenv from 'dotenv';

export class TestSubject {
  public function test() {
    console.log(dotenv); // Here I debug if I'm using a mock or not
  }
}

I'd like to mock dotenv library only for a specific test, but I'm stuck.

Here is my test suite:

import { TestSubject } from './test-subject';
import * as dotenv from 'dotenv';

jest.mock('dotenv'); // place it here, otherwise it doesn't work

describe('Suite 1', () => {
  it('test 1', () => {
    // I want to use the mock here... and it works
    const subject = new TestSubject();
    subject.test(); // shows that I'm using the mock
  });

  it('test 2', () => {
    // I don't want the mock here
    jest.dontMock('dotenv'); // doesn't work
    jest.unmock('dotenv');   // doesn't work

    const subject = new TestSubject();
    subject.test(); // shows that I'm still using the mock!!!
  });
});

As you can see, the call jest.mock('dotenv') should be placed outside describe, otherwise, it doesn't work.

I'm trying to do not use the mock in test 2, but it doesn't work: console.log shows that I'm using the mock.

Upvotes: 5

Views: 4418

Answers (2)

noam7700
noam7700

Reputation: 473

An improvement to @slideshowp2's solution.

With jest.domock you need to call require afterwards the module(like here), but the result of require has the any type and we lose type safety. so you can use typescript's dynamic imports.

Example complete code:

import * as summer from './exampleModule';

describe('mock vs no-mock', () => {
    beforeEach(() => {
        jest.resetModules();
    });

    it('NO-MOCKED', async () => {
        console.log(summer.sum(1, 1));
        expect(summer.sum(1, 1)).toBe(2);
    });
    it('MOCKED', async () => {
        jest.doMock('./exampleModule');
        const summer = await import('./exampleModule');

        const mockedSummer = mocked(summer, true);
        mockedSummer.sum.mockImplementation((a, b) => a + b + 1);
        console.log(summer.sum(1, 1));
        expect(summer.sum(1, 1)).toBe(3);
    });
    it('NO-MOCKED', async () => {
        console.log(summer.sum(1, 1));
        expect(summer.sum(1, 1)).toBe(2);
    });
});

Upvotes: 4

Lin Du
Lin Du

Reputation: 102337

Here is the solution:

index.ts, the file will be tested:

import * as dotenv from 'dotenv';

export class TestSubject {
  public test() {
    console.log(dotenv);
  }
}

Unit test:

describe('TestSubject', () => {
  beforeEach(() => {
    jest.resetModules();
  });
  it('should mock dotenv', () => {
    jest.doMock('dotenv');
    const dotenv = require('dotenv');
    const { TestSubject } = require('./');
    const subject = new TestSubject();
    subject.test();
    expect(jest.isMockFunction(dotenv.config)).toBeTruthy();
  });

  it('should not mock dotenv', () => {
    jest.unmock('dotenv');
    const dotenv = require('dotenv');
    const { TestSubject } = require('./');
    const subject = new TestSubject();
    subject.test();
    expect(jest.isMockFunction(dotenv.config)).toBeFalsy();
  });
});

Unit test result:

 PASS  src/stackoverflow/56446543/index.spec.ts
  TestSubject
    ✓ should mock dotenv (2111ms)
    ✓ should not mock dotenv (2ms)

  console.log src/stackoverflow/56446543/index.ts:5
    { config: 
       { [Function: config]
         _isMockFunction: true,
         getMockImplementation: [Function],
         mock: [Getter/Setter],
         mockClear: [Function],
         mockReset: [Function],
         mockRestore: [Function],
         mockReturnValueOnce: [Function],
         mockResolvedValueOnce: [Function],
         mockRejectedValueOnce: [Function],
         mockReturnValue: [Function],
         mockResolvedValue: [Function],
         mockRejectedValue: [Function],
         mockImplementationOnce: [Function],
         mockImplementation: [Function],
         mockReturnThis: [Function],
         mockName: [Function],
         getMockName: [Function] },
      parse: 
       { [Function: parse]
         _isMockFunction: true,
         getMockImplementation: [Function],
         mock: [Getter/Setter],
         mockClear: [Function],
         mockReset: [Function],
         mockRestore: [Function],
         mockReturnValueOnce: [Function],
         mockResolvedValueOnce: [Function],
         mockRejectedValueOnce: [Function],
         mockReturnValue: [Function],
         mockResolvedValue: [Function],
         mockRejectedValue: [Function],
         mockImplementationOnce: [Function],
         mockImplementation: [Function],
         mockReturnThis: [Function],
         mockName: [Function],
         getMockName: [Function] },
      default: 
       { config: 
          { [Function: config]
            _isMockFunction: true,
            getMockImplementation: [Function],
            mock: [Getter/Setter],
            mockClear: [Function],
            mockReset: [Function],
            mockRestore: [Function],
            mockReturnValueOnce: [Function],
            mockResolvedValueOnce: [Function],
            mockRejectedValueOnce: [Function],
            mockReturnValue: [Function],
            mockResolvedValue: [Function],
            mockRejectedValue: [Function],
            mockImplementationOnce: [Function],
            mockImplementation: [Function],
            mockReturnThis: [Function],
            mockName: [Function],
            getMockName: [Function] },
         parse: 
          { [Function: parse]
            _isMockFunction: true,
            getMockImplementation: [Function],
            mock: [Getter/Setter],
            mockClear: [Function],
            mockReset: [Function],
            mockRestore: [Function],
            mockReturnValueOnce: [Function],
            mockResolvedValueOnce: [Function],
            mockRejectedValueOnce: [Function],
            mockReturnValue: [Function],
            mockResolvedValue: [Function],
            mockRejectedValue: [Function],
            mockImplementationOnce: [Function],
            mockImplementation: [Function],
            mockReturnThis: [Function],
            mockName: [Function],
            getMockName: [Function] } } }

  console.log src/stackoverflow/56446543/index.ts:5
    { config: [Function: config],
      parse: [Function: parse],
      default: { config: [Function: config], parse: [Function: parse] } }

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        3.394s, estimated 4s

As you can see, the dotenv module is mocked for the first test case and is not mocked for the second test case.

You can find the completed demo here: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/56446543

Upvotes: 3

Related Questions