Jest重置Mock无效:多测试文件共享执行状态污染问题
Jest多测试文件Mock污染问题解决
问题场景
为依赖near-api-js、Ethersjs等外部库的工具类编写单元测试时,通过index文件统一导入执行所有测试用例,但运行发现前一测试文件的Mock未被清理,导致后续测试调用的是库的原始版本而非Mock版本。尝试过jest.clearAllMocks()、jest.resetAllMocks()以及afterEach/afterAll清理逻辑,均未解决问题。
核心原因与解决办法
1. 避免嵌套调用测试集,改用直接导入
当前index文件通过describe嵌套调用其他测试函数(如utilActivity()),会导致测试上下文共享,Mock无法隔离。正确做法是直接在index文件中导入测试文件,让Jest为每个文件创建独立上下文:
// index.test.js import './utilActivity.test.js'; import './utilNotifications.test.js'; import './utilNear.test.js';
2. 使用jest.restoreAllMocks()恢复原始实现
jest.clearAllMocks()仅清除调用记录,jest.resetAllMocks()重置Mock初始状态,但都不会恢复被jest.spyOn替换的原始方法。需在每个测试文件的顶层describe中添加:
afterEach(() => { jest.restoreAllMocks(); // 恢复所有被spyOn替换的原始方法 jest.clearAllMocks(); // 清除Mock调用记录 });
3. 重置模块缓存,避免Mock污染
对于jest.mock定义的模块Mock,若模块被缓存可能导致污染。可在测试文件顶部添加:
beforeEach(() => { jest.resetModules(); // 重置模块缓存,确保每次测试重新导入模块 });
4. 重置全局状态
针对StaticStore这类全局状态,需在每个测试前重置到初始状态:
beforeEach(() => { StaticStore.dispatch(resetAppState()); // 替换为实际的状态重置逻辑 });
修正后的测试文件示例
utilNear.test.js
jest.mock("near-api-js", () => { const originalModule = jest.requireActual("near-api-js"); return { __esModule: true, ...originalModule, KeyPair: { fromString: jest.fn().mockReturnValue("5eeb5878647747efa8d53129c85f0cfc60370049e40fc9dc2f331d51db2f58d1"), }, providers: { JsonRpcProvider: jest.fn().mockReturnValue({ query: jest.fn().mockReturnValue("access ket"), block: jest.fn().mockReturnValue({ header: { hash: "0x11111111111111" }, }), }), }, transactions: { createTransaction: jest.fn().mockImplementation(() => "neartx"), signTransaction: jest.fn().mockReturnValue(["", "5eeb5878647747efa8d53129c85f0cfc60370049e40fc9dc2f331d51db2f58d1"]), functionCall: jest.fn(), }, utils: { PublicKey: { from: jest.fn() }, serialize: { base_decode: jest.fn() }, }, }; }); describe("utils.near", () => { beforeEach(() => { jest.restoreAllMocks(); jest.clearAllMocks(); StaticStore.dispatch(resetAppState()); // 重置全局状态 jest.spyOn(CachedService, "getHashedPassword").mockReturnValue("0x4cc447191e19f3d492b3e6dc74172a6ea597c68880b62674e21af15b90022e35"); }); describe("validateAccessKey", () => { it("should return accessKey if permission is 'FullAccess'", () => { expect(utilNear.validateAccessKey(transaction, accessKey)).toBe(accessKey); }); it("should return null if transaction receiver_id differs from accessKey's receiver_id", () => { expect(utilNear.validateAccessKey(transaction, accessKey2)).toBeNull(); }); it("should return false if transaction action type is not FunctionCall", () => { expect(utilNear.validateAccessKey(transaction3, accessKey3)).toBeFalsy(); }); it("should return false if transaction methodName is included in accessKey's method_names", () => { expect(utilNear.validateAccessKey(transaction4, accessKey4)).toBeFalsy(); }); it("should return truthy if transaction methodName is not included in accessKey's method_names", () => { expect(utilNear.validateAccessKey(transaction5, accessKey5)).toBeTruthy(); }); }); });
内容的提问来源于stack exchange,提问作者Mutahhir Khan
相关产品推荐
相关产品推荐

