小编典典

如何在单个测试的基础上更改模拟实现?

all

我想通过扩展默认模拟的行为并在下一个测试执行时将其恢复为原始实现,从而在单个测试的基础上更改模拟依赖项的实现。

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

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

我目前正在使用 Jest v21。以下是典型测试的样子:

// __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. mockFn.mockImplementationOnce(fn)

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

    myMockedModule.b.mockImplementationOnce(() => ‘overridden’);

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

优点

* 第一次调用后恢复到原始实现

缺点

* 如果测试`b`多次调用它会中断
* 在未被调用之前它不会恢复到原始实现`b`(在下一个测试中泄漏)
  1. 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’
    });

优点

* 明确地重新模拟每个测试

缺点

* 无法为所有测试定义默认模拟实现
* 无法扩展默认实现强制重新声明每个模拟方法
  1. 使用 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();
    });

优点

* 完全控制模拟结果

缺点

* 大量样板代码
* 难以长期维持
  1. jest.spyOn(object, methodName)

    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?
    });

缺点

* 我无法恢复`mockImplementation`到原始的模拟返回值,因此会影响下一个测试

阅读 74

收藏
2022-07-12

共1个答案

小编典典

编写测试的一个很好的模式是创建一个设置工厂函数,它返回测试当前模块所需的数据。

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

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

describe("scenario", () => {
  beforeEach(() => {
    jest.resetModules();
  });

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

  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)
  });
});

重要的是,您 必须 重置已使用缓存的模块jest.resetModules()。这可以在beforeEach或类似的拆卸功能中完成。

有关更多信息,请参阅 jest 对象文档:https
://jestjs.io/docs/jest-object 。

2022-07-12