Jest 嘲笑测试之间的流血,重置并不能解决它
Jest mocks bleeding between tests, reset isn't fixing it
正在测试两个模块,helper
使用了 render
。 render
有可能抛出异常,所以我在 helper
中处理了这个问题,我希望进行测试以确保它按预期工作。
当我最初编写测试时,我在测试本身中编写了该测试所需的内容,包括模拟,使用 jest.doMock
。一旦所有测试都通过,我想重构以尽可能共享模拟。
所以这段代码效果很好:
test('throws', async () => {
jest.doMock('./render', () => jest.fn(async () => { throw new Error('mock error'); }));
const helper = require('./helper');
expect(async () => { helper(); }).rejects.toThrow('mock error');
expect(log_bug).toHaveBeenCalled();
});
test('succeeds', async () => {
jest.doMock('./render', () => jest.fn(async () => 'rendered result'));
const helper = require('./helper');
expect(await helper()).toEqual(true); //helper uses rendered result but doesn't return it
expect(log_bug).not.toHaveBeenCalled();
});
然而,这不是仅有的两个测试,到目前为止,模拟渲染的大多数其他测试都希望它达到 return 成功状态。我试图将成功用例重构为 __mocks__/render.js
中的一个文件,如下所示:
// __mocks__/render.js
module.exports = jest.fn(async () => 'rendered result');
然后将我的测试重构为此,使其更干:
//intention: shared reusable "success" mock for render module
jest.mock('./render');
beforeEach(() => {
jest.resetModules();
jest.resetAllMocks();
});
test('throws', async () => {
//intention: overwrite the "good" render mock with one that throws
jest.doMock('./render', () => jest.fn(async () => { throw new Error('mock error'); }));
const helper = require('./helper');
expect(async () => { await helper(); }).rejects.toThrow('mock error');
expect(log_bug).toHaveBeenCalled();
});
test('succeeds', async () => {
//intention: go back to using the "good" render mock
const helper = require('./helper');
expect(await helper()).toEqual(true); //helper uses rendered result but doesn't return it
expect(log_bug).not.toHaveBeenCalled();
});
使用这个更新的测试代码,错误记录测试仍然按预期工作——模拟被覆盖导致它抛出——但是对于下一个测试,错误再次抛出。
如果我颠倒这些测试的顺序,使模拟覆盖在最后,则不会发生失败,但这显然不是正确答案。
我做错了什么?为什么在用 doMock
覆盖它后我不能让我的模拟正确重置? doMock docs 确实说明了我正在尝试做的事情,但它们没有显示将其与普通手动模拟混合。
啊哈!我一直在挖掘并发现 ,这让我尝试了这种方法,而不是使用 jest.doMock
来覆盖测试内部:
//for this one test, overwrite the default mock to throw instead of succeed
const render = require('./render');
render.mockImplementation(async () => {
throw new Error('mock error');
});
有了这个,无论顺序如何,测试都会通过 运行!
正在测试两个模块,helper
使用了 render
。 render
有可能抛出异常,所以我在 helper
中处理了这个问题,我希望进行测试以确保它按预期工作。
当我最初编写测试时,我在测试本身中编写了该测试所需的内容,包括模拟,使用 jest.doMock
。一旦所有测试都通过,我想重构以尽可能共享模拟。
所以这段代码效果很好:
test('throws', async () => {
jest.doMock('./render', () => jest.fn(async () => { throw new Error('mock error'); }));
const helper = require('./helper');
expect(async () => { helper(); }).rejects.toThrow('mock error');
expect(log_bug).toHaveBeenCalled();
});
test('succeeds', async () => {
jest.doMock('./render', () => jest.fn(async () => 'rendered result'));
const helper = require('./helper');
expect(await helper()).toEqual(true); //helper uses rendered result but doesn't return it
expect(log_bug).not.toHaveBeenCalled();
});
然而,这不是仅有的两个测试,到目前为止,模拟渲染的大多数其他测试都希望它达到 return 成功状态。我试图将成功用例重构为 __mocks__/render.js
中的一个文件,如下所示:
// __mocks__/render.js
module.exports = jest.fn(async () => 'rendered result');
然后将我的测试重构为此,使其更干:
//intention: shared reusable "success" mock for render module
jest.mock('./render');
beforeEach(() => {
jest.resetModules();
jest.resetAllMocks();
});
test('throws', async () => {
//intention: overwrite the "good" render mock with one that throws
jest.doMock('./render', () => jest.fn(async () => { throw new Error('mock error'); }));
const helper = require('./helper');
expect(async () => { await helper(); }).rejects.toThrow('mock error');
expect(log_bug).toHaveBeenCalled();
});
test('succeeds', async () => {
//intention: go back to using the "good" render mock
const helper = require('./helper');
expect(await helper()).toEqual(true); //helper uses rendered result but doesn't return it
expect(log_bug).not.toHaveBeenCalled();
});
使用这个更新的测试代码,错误记录测试仍然按预期工作——模拟被覆盖导致它抛出——但是对于下一个测试,错误再次抛出。
如果我颠倒这些测试的顺序,使模拟覆盖在最后,则不会发生失败,但这显然不是正确答案。
我做错了什么?为什么在用 doMock
覆盖它后我不能让我的模拟正确重置? doMock docs 确实说明了我正在尝试做的事情,但它们没有显示将其与普通手动模拟混合。
啊哈!我一直在挖掘并发现 jest.doMock
来覆盖测试内部:
//for this one test, overwrite the default mock to throw instead of succeed
const render = require('./render');
render.mockImplementation(async () => {
throw new Error('mock error');
});
有了这个,无论顺序如何,测试都会通过 运行!