使用jest.restoreAllMocks与mockFn.mockReset无法恢复未模拟函数
mockRestore()无法恢复原函数实现的问题 问题描述
我正在学习Jest,尝试理解mockClear()、mockReset()和mockRestore()的用法,在calc.test.js中编写了测试代码。第一个测试通过,但第二个测试中使用add.mockRestore()或jest.restoreAllMocks()后,调用add(1,2)得到undefined而非预期的3,报错Expected:3 Received:undefined。我尝试调整mockRestore()的位置也无效。我认为mockFn.mockRestore()应恢复原实现,而使用mockReset()才会返回undefined,对此感到困惑。另外有一段无需mockRestore()的代码可正常运行,想了解如何在模拟时正确使用mockRestore()。
出错的测试代码
import {add} from "./calc"; jest.mock("./calc", () => { return { add: jest.fn(), }; }); describe("testing calculator functions", () => { test("mocking add function and testing", () => { add.mockImplementation((a, b) => a * b); expect(add(1, 2)).toBe(2); }); test("testing add without mocking", () => { add.mockRestore(); expect(add(1, 2)).toBe(3); }); });
正常运行的代码
describe("testing calculator functions", () => { test("mocking add function and testing", () => { const mockAdd = jest.fn().mockImplementation((a, b) => a * b); expect(mockAdd(1, 2)).toBe(2); }); test("testing add without mocking", () => { expect(calc.add(1, 2)).toBe(3); }); });
问题原因
你用jest.mock()手动创建了完全自定义的mock(直接返回jest.fn()),这种情况下Jest并没有保留原函数的引用,所以mockRestore()无法恢复原实现——它只能恢复那些**被Jest自动模拟(比如用jest.spyOn()创建的mock)**的函数,因为jest.spyOn()会默认保留原函数的备份。
当前的jest.mock()写法是完全替换了add为一个空的jest mock函数,没有保存原函数的任何信息,调用mockRestore()自然没有可用的原实现可以恢复,所以调用add()会返回undefined(空mock函数的默认返回值)。
正确使用mockRestore()的方法
要让mockRestore()生效,你需要用jest.spyOn()来创建mock,它会自动保留原函数的引用,之后可以通过mockRestore()恢复。或者在jest.mock()中手动保留原函数的引用,不过更推荐前者。
方法1:使用jest.spyOn()
import * as calc from "./calc"; describe("testing calculator functions", () => { test("mocking add function and testing", () => { // 创建spy并替换实现 const addSpy = jest.spyOn(calc, 'add').mockImplementation((a, b) => a * b); expect(calc.add(1, 2)).toBe(2); }); test("testing add without mocking", () => { // 恢复原函数 jest.spyOn(calc, 'add').mockRestore(); expect(calc.add(1, 2)).toBe(3); }); });
更简洁的方式:在每个测试后自动恢复所有mock
import * as calc from "./calc"; describe("testing calculator functions", () => { // 每个测试后自动恢复所有mock afterEach(() => { jest.restoreAllMocks(); }); test("mocking add function and testing", () => { jest.spyOn(calc, 'add').mockImplementation((a, b) => a * b); expect(calc.add(1, 2)).toBe(2); }); test("testing add without mocking", () => { expect(calc.add(1, 2)).toBe(3); }); });
方法2:在jest.mock()中手动保留原函数
如果你一定要用jest.mock(),可以通过jest.requireActual()获取原模块的内容,保留原函数的引用:
import {add} from "./calc"; jest.mock("./calc", () => { // 获取原模块的真实实现 const originalModule = jest.requireActual("./calc"); return { ...originalModule, // 创建一个spy(会保留原函数) add: jest.fn(originalModule.add), }; }); describe("testing calculator functions", () => { test("mocking add function and testing", () => { add.mockImplementation((a, b) => a * b); expect(add(1, 2)).toBe(2); }); test("testing add without mocking", () => { add.mockRestore(); expect(add(1, 2)).toBe(3); }); });
总结
mockRestore()仅对由jest.spyOn()创建的mock或手动保留了原函数引用的mock生效,因为它需要原函数的备份才能恢复。- 直接用
jest.mock()返回空jest.fn()时,没有原函数备份,mockRestore()无法工作,调用后函数依然是空mock,返回undefined。 - 推荐使用
jest.spyOn()搭配afterEach(jest.restoreAllMocks())的方式,更简洁且不易出错。
内容的提问来源于stack exchange,提问作者Gautham Muralidharan

