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:

  1. No Vitest support - GitHub Issue confirms it doesn’t work with Vitest
  2. Requires tsc compiler - Won’t work with esbuild or swc (which Vitest uses)
  3. Development stopped - Maintainers stopped new features because TypeScript team won’t improve transformer DX
  4. Complex setup - Requires TypeScript transformers which add build complexity

Best Alternative: vitest-mock-extended

Installation

Terminal window
npm install vitest-mock-extended --save-dev

Basic 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 mock
const mockUserService = mock<UserService>();
// Use in tests
mockUserService.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

2. Suites (formerly Automock)

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

Featurets-auto-mockvitest-mock-extendedIntermock
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):

  1. Install vitest-mock-extended - drop-in replacement with similar API
  2. Replace createMock<T>() with mock<T>() - nearly identical syntax
  3. 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.

Sources