每次测试的 Jest Mock module

IT技术 javascript react-native unit-testing jestjs mocking
2021-03-16 19:34:58

我对 Jest 中的模拟以及如何对实现进行单元测试感到非常困惑。问题是我想模拟不同的预期行为。

有没有办法实现这一目标?因为导入只能在文件的顶部,并且为了能够模拟某些东西,它必须在导入之前声明。我还尝试传递一个本地函数,以便我可以覆盖该行为,但开玩笑说你不允许传递任何本地函数。

jest.mock('the-package-to-mock', () => ({
  methodToMock: jest.fn(() => console.log('Hello'))
}));

import * as theThingToTest from '../../../app/actions/toTest'
import * as types from '../../../app/actions/types'

it('test1', () => {
  expect(theThingToTest.someAction().type).toBe(types.SOME_TYPE)
})

it('test2', () => {
  //the-package-to-mock.methodToMock should behave like something else
  expect(theThingToTest.someAction().type).toBe(types.SOME_TYPE)
})

您可以想象在内部theThingToTest.someAction()使用the-package-to-mock.methodToMock

5个回答

您可以使用间谍进行模拟并导入模拟module。在您的测试中,您可以使用mockImplementation以下命令设置模拟的行为方式

jest.mock('the-package-to-mock', () => ({
  methodToMock: jest.fn()
}));
import {methodToMock} from 'the-package-to-mock'

it('test1', () => {
  methodToMock.mockImplementation(() => 'someValue')
})

it('test2', () => {
   methodToMock.mockImplementation(() => 'anotherValue')
})
为什么需要模拟一个常量值。无论如何,只需替换jest.fn()为值即可。也不需要importmodule了。
2021-04-21 19:34:58
mockImplementation仅当您忘记将其分配给jest.fn(). 我刚测试
2021-04-22 19:34:58
如果methodToMock是一个常量属性呢?
2021-04-30 19:34:58
当我尝试这个方法时,我得到 mockImplementation() 不是一个函数。
2021-05-05 19:34:58
非常感谢您的回答,它就像一个魅力:)
2021-05-07 19:34:58

我使用以下模式:

'use strict'

const packageToMock = require('../path')

jest.mock('../path')
jest.mock('../../../../../../lib/dmp.db')

beforeEach(() => {
  packageToMock.methodToMock.mockReset()
})

describe('test suite', () => {
  test('test1', () => {
    packageToMock.methodToMock.mockResolvedValue('some value')
    expect(theThingToTest.someAction().type).toBe(types.SOME_TYPE)

  })
  test('test2', () => {
    packageToMock.methodToMock.mockResolvedValue('another value')
    expect(theThingToTest.someAction().type).toBe(types.OTHER_TYPE)
  })
})

解释:

您模拟您尝试在测试套件级别使用的类,确保在每次测试之前重置模拟,并且对于每个测试,您使用 mockResolveValue 来描述返回模拟时将返回的内容

另一种方法是使用jest.doMock(moduleName, factory, options)

例如

the-package-to-mock.ts

export function methodToMock() {
  return 'real type';
}

toTest.ts

import { methodToMock } from './the-package-to-mock';

export function someAction() {
  return {
    type: methodToMock(),
  };
}

toTest.spec.ts

describe('45006254', () => {
  beforeEach(() => {
    jest.resetModules();
  });
  it('test1', () => {
    jest.doMock('./the-package-to-mock', () => ({
      methodToMock: jest.fn(() => 'type A'),
    }));
    const theThingToTest = require('./toTest');
    expect(theThingToTest.someAction().type).toBe('type A');
  });

  it('test2', () => {
    jest.doMock('./the-package-to-mock', () => ({
      methodToMock: jest.fn(() => 'type B'),
    }));
    const theThingToTest = require('./toTest');
    expect(theThingToTest.someAction().type).toBe('type B');
  });
});

单元测试结果:

 PASS  examples/45006254/toTest.spec.ts
  45006254
    ✓ test1 (2016 ms)
    ✓ test2 (1 ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 toTest.ts |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        3.443 s

源代码:https : //github.com/mrdulin/jest-v26-codelab/tree/main/examples/45006254

这很好,但只有一个小缺点,它不一定适合 ide 集成
2021-05-19 19:34:58

spyOn对我们来说效果最好。看之前的回答:

https://stackoverflow.com/a/54361996/1708297

安德烈亚斯的回答与函数配合得很好,这是我使用它发现的:

// You don't need to put import line after the mock.
import {supportWebGL2} from '../utils/supportWebGL';


// functions inside will be auto-mocked
jest.mock('../utils/supportWebGL');
const mocked_supportWebGL2 = supportWebGL2 as jest.MockedFunction<typeof supportWebGL2>;

// Make sure it return to default between tests.
beforeEach(() => {
  // set the default
  supportWebGL2.mockImplementation(() => true); 
});

it('display help message if no webGL2 support', () => {
  // only for one test
  supportWebGL2.mockImplementation(() => false);

  // ...
});

如果您的模拟module不是函数,它将不起作用。我无法仅针对一项测试更改导出布尔值的模拟:/。我的建议是,重构一个函数,或者制作另一个测试文件。

export const supportWebGL2 = /* () => */ !!window.WebGL2RenderingContext;
// This would give you: TypeError: mockImplementation is not a function