如何在Jest中修改单例类内部函数的Mock返回值?
问题描述
我在应用类中引入了模块内的单例类MySingleton,想要修改其内部函数isDecision的Mock返回值。尝试过两种方式但均未成功,不清楚修改单例内部函数Mock实现的正确流程。
尝试过的两种方式:
- 监听单例:
jest.spyOn(MySingleton, "getInstance").mockImplementation({ isDecision: jest.fn(() => false), });
- 内联调用Mock:
MySingleton.getInstance().isDecision.mockReturnValue(false);
单例类结构
export default class MySingleton { private static _instance: MySingleton; static getInstance(): MySingleton { if (!MySingleton._instance) { MySingleton._instance = new MySingleton(); } return MySingleton._instance; } isDecision(): boolean { return true; } }
消费类代码
import { MySingleton } from "@my-singleton"; export class Consumer { private _singleton: MySingleton; constructor() { this._singleton = MySingleton.getInstance(); } utilityFunction(): boolean { return this._singleton.isDecision(); } }
测试代码
jest.mock("@my-singleton", () => ({ __esModule: true, MySingleton: { getInstance: jest.fn().mockImplementation(() => ({ isDecision: jest.fn(() => true), })), }, })); describe("Consumer", () => { let consumer; beforeEach(() => { consumer = new Consumer(); }); describe("test decision is true case", () => { test("should return true", () => { const decision = consumer.utilityFunction(); expect(decision).toBe(true); }); }); describe("test decision is false case", () => { beforeEach(() => { // 如何修改单例的IsDecsion使其返回false? }); test("should return false", () => { const decision = consumer.utilityFunction(); expect(decision).toBe(false); }); }); });
请问如何在测试的beforeEach中修改isDecision的Mock返回值为false?
解决方案
核心问题在于当前jest.mock的实现导致getInstance的返回对象无法被稳定修改,或者修改时机不对。以下是两种可行的解决方法:
方法一:固定Mock单例实例
提前创建一个包含isDecision Mock函数的对象,让getInstance始终返回该对象,这样修改isDecision的返回值会直接作用到所有使用该实例的地方:
// 提前定义全局Mock单例对象 const mockSingletonInstance = { isDecision: jest.fn(() => true), }; // 修改jest.mock的实现,让getInstance返回固定的Mock对象 jest.mock("@my-singleton", () => ({ __esModule: true, MySingleton: { getInstance: jest.fn().mockReturnValue(mockSingletonInstance), }, })); describe("Consumer", () => { let consumer; beforeEach(() => { // 每次测试前重置isDecision的默认返回值为true,避免测试污染 mockSingletonInstance.isDecision.mockReturnValue(true); consumer = new Consumer(); }); describe("test decision is true case", () => { test("should return true", () => { const decision = consumer.utilityFunction(); expect(decision).toBe(true); }); }); describe("test decision is false case", () => { beforeEach(() => { // 修改isDecision的返回值为false mockSingletonInstance.isDecision.mockReturnValue(false); }); test("should return false", () => { const decision = consumer.utilityFunction(); expect(decision).toBe(false); }); }); });
这种方式的优势是实例唯一,修改一次就能全局生效,同时外层beforeEach会在每次测试前重置默认值,避免不同测试用例之间的干扰。
方法二:调整Mock修改时机
如果不想固定实例,需要确保修改isDecision的操作在创建Consumer实例之前执行,这样Consumer构造函数调用getInstance时,拿到的就是已经修改过返回值的Mock对象:
jest.mock("@my-singleton", () => ({ __esModule: true, MySingleton: { getInstance: jest.fn().mockImplementation(() => ({ isDecision: jest.fn(() => true), })), }, })); describe("Consumer", () => { let consumer; // 外层beforeEach只重置getInstance的Mock实现,不创建Consumer beforeEach(() => { MySingleton.getInstance.mockImplementation(() => ({ isDecision: jest.fn(() => true), })); }); describe("test decision is true case", () => { beforeEach(() => { consumer = new Consumer(); }); test("should return true", () => { const decision = consumer.utilityFunction(); expect(decision).toBe(true); }); }); describe("test decision is false case", () => { beforeEach(() => { // 先获取Mock实例,修改isDecision返回值,再创建Consumer const mockInstance = MySingleton.getInstance(); mockInstance.isDecision.mockReturnValue(false); consumer = new Consumer(); }); test("should return false", () => { const decision = consumer.utilityFunction(); expect(decision).toBe(false); }); }); });
原方法失败原因
- 第一种方式错误:
mockImplementation需要传入函数,你直接传了对象,正确写法应为mockImplementation(() => ({ isDecision: jest.fn(() => false) })),但即便修正,若Consumer实例已创建,它引用的还是之前的实例,修改不会生效。 - 第二种方式时机错误:如果
Consumer实例在修改isDecision之前就已创建,它引用的是旧的isDecision函数,新修改的是后续调用getInstance返回的对象,无法影响已存在的Consumer实例。
内容的提问来源于stack exchange,提问作者Deviland
相关产品推荐
相关产品推荐

