0

I am aware of how to mock/spy an ES6 import with jest, but this one gets around my head:

my-module.ts

import minimatch from 'minimatch';

export function foo(pattern: string, str: string): boolean {
  return minimatch(pattern, str);
}

test.ts:

describe('minimatch', () => {
  it('should call minimatch', () => {
    const mock = jest.fn().mockReturnValue(true);
    jest.mock('minimatch', mock);

    foo('*', 'hello');

    expect(mock).toHaveBeenCalled();
  });
});

I've also tried mocking in different way:

import * as minimatch from 'minimatch';
// ...
const mock = jest.fn().mockReturnValue(true);
(minimatch as any).default = mock;

Or even

import {mockModule} from '../../../../../../test/ts/utils/jest-utils';
// ...
const mock = jest.fn().mockReturnValue(true);
const originalModule = jest.requireActual('minimatch');
jest.mock('minimatch', () => Object.assign({}, originalModule, mockModule));

My test fails with all of the above ways to mock.

Lin Du
  • 88,126
  • 95
  • 281
  • 483
user5365075
  • 2,094
  • 2
  • 25
  • 42

1 Answers1

0

You can't use jest.mock() inside test case functional scope. You should use it in the module scope.

E.g. my-module.ts:

import minimatch from 'minimatch';

export function foo(pattern: string, str: string): boolean {
  return minimatch(pattern, str);
}

my-module.test.ts:

import { foo } from './my-module';
import minimatch from 'minimatch';

jest.mock('minimatch', () => jest.fn());

describe('minimatch', () => {
  it('should call minimatch', () => {
    foo('*', 'hello');
    expect(minimatch).toHaveBeenCalled();
  });
});

Unit test results with 100% coverage:

 PASS  stackoverflow/60350522/my-module.test.ts
  minimatch
    ✓ should call minimatch (6ms)

--------------|---------|----------|---------|---------|-------------------
File          | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------|---------|----------|---------|---------|-------------------
All files     |     100 |      100 |     100 |     100 |                   
 my-module.ts |     100 |      100 |     100 |     100 |                   
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.304s, estimated 6s

If you want to mock module inside test case, you should use jest.doMock(moduleName, factory, options).

E.g.

my-module.test.ts:

describe('minimatch', () => {
  it('should call minimatch', () => {
    jest.doMock('minimatch', () => jest.fn());
    const { foo } = require('./my-module');
    const minimatch = require('minimatch');
    foo('*', 'hello');
    expect(minimatch).toHaveBeenCalled();
  });
});
Lin Du
  • 88,126
  • 95
  • 281
  • 483
  • Quite clear. The scope was the issue, should probably be emphasized more in the docs. The second approach with `doMock` is syntactically odd, but good to know as sometimes you don't want other test cases in your file to use the real implementation. Would there be a way to `spyOn` this module ? – user5365075 Feb 24 '20 at 11:46