如何用Jest替换Mock TypeScript类中的只读属性
解决TypeScript中Jest Mock只读依赖属性的问题
先修正原代码的笔误:Consumer#useValue中的this.svc应为this.broker.svc,否则会直接报错。
给定类结构:
// value-service.ts export class ValueService { fetchValue(): Promise<string> { ... } }
// broker.ts import {ValueService} from './value-service.js'; export class Broker { public readonly svc = new ValueService(); }
// consumer.ts import {Broker} from './broker.js'; export class Consumer { constructor (private readonly broker: Broker) { } public async useValue() { const val = await this.broker.svc.fetchValue(); return `${val}123`; } }
在测试Consumer#useValue时,需要MockValueService#fetchValue,但Broker的svc是只读属性,自动Mock后出现运行时/类型问题,以下是无需自定义jest.mock工厂函数的简洁解决方案:
方法1:通过Object.defineProperty设置只读属性
利用JavaScript属性描述符,给Mock的Broker实例手动赋值svc,同时保持属性的只读特性:
import {jest} from '@jest/globals'; import {ValueService} from './value-service.js'; import {Broker} from './broker.js' import {Consumer} from './consumer.js'; // 保留自动Mock jest.mock('./value-service.js'); jest.mock('./broker.js'); describe('Consumer', () => { let instance: Consumer; beforeEach(() => { // 创建并配置Mock的ValueService const mockValueSvc = jest.mocked(new ValueService()); mockValueSvc.fetchValue.mockResolvedValue('abc'); // 创建Broker Mock实例 const broker = jest.mocked(new Broker()); // 给只读属性svc赋值 Object.defineProperty(broker, 'svc', { value: mockValueSvc, configurable: true // 允许后续修改(可选) }); instance = new Consumer(broker); }); it('uses the value correctly', async () => { expect(await instance.useValue()).toBe('abc123'); }); });
方法2:类型断言绕过TypeScript编译检查
如果不需要严格维护属性的只读特性,直接用类型断言跳过TypeScript的readonly约束:
// 在beforeEach中替换Object.defineProperty为: (broker as any).svc = mockValueSvc; // 或者更精确的类型断言: (broker as Omit<Broker, 'svc'> & { svc: ValueService }).svc = mockValueSvc;
关键说明
- 自动Mock的Broker实例默认不会初始化
svc属性,因此需要手动赋值 - TypeScript的
readonly仅为编译时约束,运行时可通过属性描述符或类型断言修改 - 需先配置好
ValueService的Mock返回值,再赋值给Broker的svc,确保测试逻辑正确
内容的提问来源于stack exchange,提问作者Coderer
相关产品推荐
相关产品推荐

