我想通过扩展默认模拟&在单一测试基础上更改模拟依赖项的实现当以下测试执行时,#39;行为和将其恢复到原始实现。
更简单地说,这就是我想要实现的目标:
我目前正在使用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 eeffects
});
it('should revert back to default myMockedModule mock', () => {
myMockedModule.a(); // === true
myMockedModule.b(); // === true
});
});
我尝试了一些策略,但我找不到任何可以定义令人满意的解决方案。
优点
缺点
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'
});
优点
缺点
码
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'
});
优点
缺点
码
__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();
});
缺点
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 the original mocked value?
});
提前感谢您提出任何意见或建议!
答案 0 :(得分:12)
编写测试的一个很好的模式是创建一个设置工厂函数,它返回测试当前模块所需的数据。
下面是您的第二个示例之后的一些示例代码,但允许以可重用的方式提供默认值和覆盖值。
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)
})
});
答案 1 :(得分:3)
聚会晚一点,但是如果有人对此有疑问。
我们使用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
答案 2 :(得分:2)
使用mockFn.mockImplementation(fn)。
将默认实现放入beforeEach
中。每次测试之前,模拟都会重置为此。
要覆盖,请在测试中使用mockImplementation
。
这将覆盖测试中任何/所有调用的模拟行为,并且在下一次测试之前将被beforeEach
实现覆盖。
例如:
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 */ });
// ...
});
答案 3 :(得分:0)
在模拟单个方法时(需要保持类/模块实现的其余部分完整无缺),我发现以下方法有助于重置单个测试中的任何实现调整。
我发现这种方法是最简洁的方法,无需在文件开头 jest.mock
等。您只需要在下面看到的代码来模拟 MyClass.methodName
。另一个优点是默认情况下 spyOn
保留原始方法实现,但也保存所有统计信息(调用数量、参数、结果等)以进行测试,并且在某些情况下必须保留默认实现。因此,您可以灵活地保留默认实现或通过简单添加 .mockImplementation
来更改它,如下面的代码中所述。
代码在 Typescript 中,注释突出了 JS 的差异(准确地说,差异在一行中)。使用 Jest 26.6 测试。
describe('test set', () => {
let mockedFn: jest.SpyInstance<void>; // void is the return value of the mocked function, change as necessary
// For plain JS use just: let mockedFn;
beforeEach(() => {
mockedFn = jest.spyOn(MyClass.prototype, 'methodName');
// Use the following instead if you need not to just spy but also to replace the default method implementation:
// mockedFn = jest.spyOn(MyClass.prototype, 'methodName').mockImplementation(() => {/*custom implementation*/});
});
afterEach(() => {
// Reset to the original method implementation (non-mocked) and clear all the mock data
mockedFn.mockRestore();
});
it('does first thing', () => {
/* Test with the default mock implementation */
});
it('does second thing', () => {
mockedFn.mockImplementation(() => {/*custom implementation just for this test*/});
/* Test utilising this custom mock implementation. It is reset after the test. */
});
it('does third thing', () => {
/* Another test with the default mock implementation */
});
});