research-20260127-162922-cd8452
TL;DR
Recommended: Use vitest-mock-extended - it’s the direct equivalent of ts-auto-mock for Vitest, providing type-safe interface mocking without TypeScript transformers.
The Problem with ts-auto-mock
ts-auto-mock has significant limitations:
- No Vitest support - GitHub Issue #1271 confirms it doesn’t work with Vitest
- Requires tsc compiler - Won’t work with esbuild or swc (which Vitest uses)
- Development stopped - Maintainers stopped new features because TypeScript team won’t improve transformer DX
- Complex setup - Requires TypeScript transformers which add build complexity
Best Alternative: vitest-mock-extended
Installation
npm install vitest-mock-extended --save-devBasic Usage
import { mock } from 'vitest-mock-extended';
interface UserService { getUser: (id: string) => Promise<User>; updateUser: (user: User) => Promise<void>; deleteUser: (id: string) => Promise<boolean>;}
// Create a fully typed mockconst mockUserService = mock<UserService>();
// Use in testsmockUserService.getUser.mockResolvedValue({ id: '1', name: 'John' });Advanced Features
1. Argument-specific return values with calledWith()
mockUserService.getUser .calledWith('admin') .mockResolvedValue({ id: 'admin', name: 'Admin', role: 'admin' });
mockUserService.getUser .calledWith('user') .mockResolvedValue({ id: 'user', name: 'User', role: 'user' });2. Deep mocking with mockDeep()
import { mockDeep } from 'vitest-mock-extended';
interface Database { connection: { query: (sql: string) => Promise<any>; close: () => void; };}
const mockDb = mockDeep<Database>();mockDb.connection.query.mockResolvedValue([{ id: 1 }]);3. Matchers for flexible assertions
import { mock, anyString, anyNumber } from 'vitest-mock-extended';
mockService.process .calledWith(anyString(), anyNumber()) .mockReturnValue('processed');Available matchers:
any(),anyString(),anyNumber(),anyBoolean()anyFunction(),anyObject(),anyArray()isA(ClassName),includes(value),notNull()
4. Argument capture
import { mock, captor } from 'vitest-mock-extended';
const argCaptor = captor<User>();mockService.save(argCaptor);
expect(argCaptor.value).toEqual({ name: 'John' });Type Safety
import { MockProxy } from 'vitest-mock-extended';
let mockService: MockProxy<UserService>;
beforeEach(() => { mockService = mock<UserService>();});Other Alternatives
1. Google Intermock
- Creates mock objects with fake data using Faker
- CLI-based, good for generating test fixtures
- github.com/google/intermock
2. Suites (formerly Automock)
- Full DI-aware testing framework
- Works with NestJS, InversifyJS
- Supports Jest, Vitest, Sinon
- github.com/suites-dev/suites
3. Built-in Vitest mocking
For simpler cases, Vitest’s vi.fn() with vi.mocked() works:
import { vi } from 'vitest';
const mockFn = vi.fn<[string], Promise<User>>();vi.mocked(mockFn).mockResolvedValue({ id: '1', name: 'Test' });Comparison Table
| Feature | ts-auto-mock | vitest-mock-extended | Intermock |
|---|---|---|---|
| Vitest support | ❌ No | ✅ Yes | ✅ Yes |
| Interface mocking | ✅ Yes | ✅ Yes | ✅ Yes |
| Deep mocking | ✅ Yes | ✅ Yes | ❌ No |
| Requires transformer | ✅ Yes | ❌ No | ❌ No |
| Fake data generation | ❌ No | ❌ No | ✅ Yes |
| Active development | ⚠️ Stopped | ✅ Active | ⚠️ Limited |
Recommendation
For your use case (migrating from ts-auto-mock to Vitest):
- Install vitest-mock-extended - drop-in replacement with similar API
- Replace
createMock<T>()withmock<T>()- nearly identical syntax - Use
mockDeep<T>()for nested objects - equivalent to ts-auto-mock’s deep mocking
The migration should be straightforward since both libraries use similar patterns for creating type-safe mocks from TypeScript interfaces.