You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 11:23:19