如何在每个测试基础上更改模拟实现[Jestjs]


86

我想改变嘲笑依赖的实施在每个单一的测试基础上通过扩展默认模仿的行为,并恢复回原来的执行下一个测试执行时。

更简单地说,这就是我要实现的目标:

  1. 模拟依赖
  2. 在单个测试中更改/扩展模拟实现
  3. 下一次测试执行时恢复为原始模拟

我目前正在使用Jest v21

典型的Jest测试如下所示:

__mocks__/myModule.js

const myMockedModule = jest.genMockFromModule('../myModule');

myMockedModule.a = jest.fn(() => true);
myMockedModule.b = jest.fn(() => true);

export default myMockedModule;

__tests__/myTest.js

import myMockedModule from '../myModule';

// Mock myModule
jest.mock('../myModule');

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

describe('MyTest', () => {
  it('should test with default mock', () => {
    myMockedModule.a(); // === true
    myMockedModule.b(); // === true
  });

  it('should override myMockedModule.b mock result (and leave the other methods untouched)', () => {
    // Extend change mock
    myMockedModule.a(); // === true
    myMockedModule.b(); // === 'overridden'
    // Restore mock to original implementation with no side effects
  });

  it('should revert back to default myMockedModule mock', () => {
    myMockedModule.a(); // === true
    myMockedModule.b(); // === true
  });
});

到目前为止,这是我尝试过的:


1-模拟Fn.mockImplementationOnce(fn)

优点

  • 首次调用后恢复为原始实现

缺点

  • 如果b多次调用,它将中断
  • 直到b不被调用,它才会恢复为原始实现(在下一个测试中消失)

码:

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  myMockedModule.b.mockImplementationOnce(() => 'overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});

2- jest.doMock(moduleName,factory,options)

优点

  • 每次测试都明确地重新模拟

缺点

  • 无法为所有测试定义默认的模拟实现
  • 无法扩展默认实现强制重新声明每个模拟方法

码:

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  jest.doMock('../myModule', () => {
    return {
      a: jest.fn(() => true,
      b: jest.fn(() => 'overridden',
    }
  });

  myModule.a(); // === true
  myModule.b(); // === 'overridden'
});

3 -手动嘲讽与setter方法(如解释在这里

优点

  • 完全控制模拟结果

缺点

  • 很多样板代码
  • 长期难以维持

码:

__mocks__/myModule.js

const myMockedModule = jest.genMockFromModule('../myModule');

let a = true;
let b = true;

myMockedModule.a = jest.fn(() => a);
myMockedModule.b = jest.fn(() => b);

myMockedModule.__setA = (value) => { a = value };
myMockedModule.__setB = (value) => { b = value };
myMockedModule.__reset = () => {
  a = true;
  b = true;
};
export default myMockedModule;

__tests__/myTest.js

it('should override myModule.b mock result (and leave the other methods untouched)', () => {
  myModule.__setB('overridden');

  myModule.a(); // === true
  myModule.b(); // === 'overridden'

  myModule.__reset();
});

4- jest.spyOn(对象,methodName)

缺点

  • 我无法还原回mockImplementation原始的模拟返回值,因此会影响下一个测试

码:

beforeEach(() => {
  jest.clearAllMocks();
  jest.restoreAllMocks();
});

// Mock myModule
jest.mock('../myModule');

it('should override myModule.b mock result (and leave the other methods untouched)', () => {

  const spy = jest.spyOn(myMockedModule, 'b').mockImplementation(() => 'overridden');

  myMockedModule.a(); // === true
  myMockedModule.b(); // === 'overridden'

  // How to get back to original mocked value?
});

真好 但是,如何为npm模块(如“ @ private-repo / module”)选择选项2?我看到的大多数示例都有相对路径?这也适用于已安装的模块吗?
mrbinky3000 '19

Answers:


47

编写测试的一个不错的模式是创建一个设置工厂功能,该功能返回测试当前模块所需的数据。

下面是第二个示例之后的一些示例代码,尽管允许以可重用的方式提供默认值和替代值。

const spyReturns = returnValue => jest.fn(() => returnValue);

describe("scenario", () => {
  const setup = (mockOverrides) => {
    const mockedFunctions =  {
      a: spyReturns(true),
      b: spyReturns(true),
      ...mockOverrides
    }
    return {
      mockedModule: jest.doMock('../myModule', () => mockedFunctions)
    }
  }

  it("should return true for module a", () => {
    const { mockedModule } = setup();
    expect(mockedModule.a()).toEqual(true)
  });

  it("should return override for module a", () => {
    const EXPECTED_VALUE = "override"
    const { mockedModule } = setup({ a: spyReturns(EXPECTED_VALUE)});
    expect(mockedModule.a()).toEqual(EXPECTED_VALUE)
  });
});

40

香草JS

使用mockFn.mockImplementation(fn)

import { funcToMock } from './somewhere';
jest.mock('./somewhere');

beforeEach(() => {
  funcToMock.mockImplementation(() => { /* default implementation */ });
});

test('case that needs a different implementation of funcToMock', () => {
  funcToMock.mockImplementation(() => { /* implementation specific to this test */ });
  // ...
});

打字稿

为了防止出现消息ockImplementation不是funcToMock的属性,您将需要指定类型,例如,将顶行从上面更改为以下内容:

import { (funcToMock as jest.Mock) } from './somewhere';

可以在此处找到解决此问题的问题:类型上不存在jest typescript属性模拟


21

晚会晚了一点,但是如果有人对此有疑问。

我们使用TypeScript,ES6和babel进行本机开发。

我们通常在根__mocks__目录中模拟外部NPM模块。

我想覆盖aws-amplify的Auth类中模块的特定功能以进行特定测试。

    import { Auth } from 'aws-amplify';
    import GetJwtToken from './GetJwtToken';
    ...
    it('When idToken should return "123"', async () => {
      const spy = jest.spyOn(Auth, 'currentSession').mockImplementation(() => ({
        getIdToken: () => ({
          getJwtToken: () => '123',
        }),
      }));

      const result = await GetJwtToken();
      expect(result).toBe('123');
      spy.mockRestore();
    });

要点:https : //gist.github.com/thomashagstrom/e5bffe6c3e3acec592201b6892226af2

教程:https//medium.com/p/b4ac52a005d#19c5

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.