如何使用Vitest对TypeScript类进行Mock?
问题
我有一个类FormattedStore,它从其他模块导入Formatter类并在构造函数中实例化。在测试FormattedStore时,需要MockFormatter的部分方法,但当前场景无法使用构造注入,只能通过模块Mock实现。
示例代码如下:
// Formatter.ts export class Formatter { format(msg: string): string { return `--- [INFO] ${msg}\n`; } } // FormattedStore.ts import {Formatter} from '@/Formatter' export class FormattedStore implements Store { public store: string[]; constructor() { // 修正原代码拼写错误:construct → constructor this.formatter = new Formatter(); this.store = []; } write(msg: string): void { this.store.push(this.formatter.format(msg)); } } // FormattedStore.spec.ts import {FormattedStore} from '@/FormattedStore'; import {Formatter} from '@/Formatter' import {vi, expect, test} from 'vitest'; vi.mock( '@/Formatter', () => ({ Formatter: vi.fn() // 正确且类型安全的Mock方式是什么? })); test('it stores formatted message', () => { // 如何在此处Mock formatter,使其返回'formatted test message'? const store = new FormattedStore(); store.write('TestMessage'); expect(store.store[0]).toBe('formatted test message'); })
解决方案
1. 类型安全的模块Mock写法
使用Vitest的vi.mock时,需要保证Mock的Formatter类结构与原类一致,避免类型错误。通过mockImplementation让Mock类实例返回包含Mock方法的对象:
vi.mock('@/Formatter', () => { return { Formatter: vi.fn().mockImplementation(() => ({ // 明确参数和返回值类型,保证类型安全 format: vi.fn<string, [string]>() })) }; });
2. 控制Mock方法的返回值
在测试用例中,有两种常用方式指定format方法的返回值:
方式一:通过vi.spyOnMock原型方法
直接控制Formatter原型上的方法,所有实例都会使用该Mock实现,简洁易维护:
test('it stores formatted message', () => { // Mock format方法并指定返回值 const formatSpy = vi.spyOn(Formatter.prototype, 'format').mockReturnValue('formatted test message'); const store = new FormattedStore(); store.write('TestMessage'); // 验证存储结果 expect(store.store[0]).toBe('formatted test message'); // 验证方法调用情况 expect(formatSpy).toHaveBeenCalledExactlyOnceWith('TestMessage'); });
方式二:直接操作Mock实例的方法
如果需要针对特定实例Mock,可先获取Mock实例再设置方法返回值:
test('it stores formatted message', () => { // 获取Mock的Formatter实例,通过类型断言保证类型正确 const mockFormatter = new Formatter() as { format: vi.Mock<string, [string]> }; // 指定format方法返回值 mockFormatter.format.mockReturnValue('formatted test message'); const store = new FormattedStore(); store.write('TestMessage'); expect(store.store[0]).toBe('formatted test message'); expect(mockFormatter.format).toHaveBeenCalledWith('TestMessage'); });
完整测试代码
// FormattedStore.spec.ts import { FormattedStore } from '@/FormattedStore'; import { Formatter } from '@/Formatter' import { vi, expect, test } from 'vitest'; // 模块Mock:保证类型安全 vi.mock('@/Formatter', () => { return { Formatter: vi.fn().mockImplementation(() => ({ format: vi.fn<string, [string]>() })) }; }); test('it stores formatted message', () => { const formatSpy = vi.spyOn(Formatter.prototype, 'format').mockReturnValue('formatted test message'); const store = new FormattedStore(); store.write('TestMessage'); expect(store.store[0]).toBe('formatted test message'); expect(formatSpy).toHaveBeenCalledExactlyOnceWith('TestMessage'); });
内容的提问来源于stack exchange,提问作者chiborg
相关产品推荐
相关产品推荐

