@corez/mock
A powerful TypeScript decorator-style mocking library with type-safe mock and spy capabilities.
Features
- 🔒 Fully type-safe API with TypeScript
- 🎭 Class and method decorators support
- 🎪 Multiple mocking patterns (function, class, method)
- 🎲 Built-in faker data generation
- 💾 Smart caching system with instance isolation
- 🔄 Jest-style API support
- 🎯 Getter/Setter mocking support
- 📦 Zero runtime dependencies
- 🔍 Comprehensive error handling
Installation
pnpm add @corez/mock
Requirements
- Node.js 16.0.0 or later
- TypeScript 5.0.0 or later
Peer Dependencies
{
"@faker-js/faker": "^8.0.0"
}
Imports
The library provides several ways to import its functionality:
import mock from '@corez/mock';
import {
Mock,
MockedFunction,
MockedObject,
MockedClass,
DecoratorError,
type MockConfig,
type FakerOptions,
} from '@corez/mock';
import * as MockLib from '@corez/mock';
Exports Structure
The library exports are organized as follows:
-
Default Export
mock
: The main mock instance with core functionality
-
Types and Interfaces (from ./types
)
- Core types:
Constructor
, DeepPartial
, PromiseOrValue
- Mock function types:
MockedFunction
, AsyncMockedFunction
- Mock object types:
MockedObject
, MockedClass
- Configuration types:
MockConfig
, MockConfigObject
, MockOptions
- Pattern types:
MockValue
, MockMultipleConfig
- Faker types:
Faker
, FakerOptions
- Jest adapter types:
JestMatcherExtensions
, JestMockedFunction
-
Decorators and Errors (from ./decorators
)
Mock
: The main decorator with all its variantsDecoratorError
: Error class for decorator-related issues
All exports are available directly from the package root, making it easy to import any combination of functionality you
need.
Basic Usage
Class Mocking
The @Mock()
decorator can be used to mock an entire class:
interface User {
id: number;
name: string;
}
@Mock()
class UserService {
getUser(id: number): User {
return {id, name: 'John'};
}
get userCount(): number {
return this.users.length;
}
}
const service = new UserService();
service.getUser(1);
service.userCount;
const getUserMock = service.getUser as MockedFunction<[number], User>;
console.log(getUserMock.mock.calls);
Method and Getter Mocking
Individual methods and getters can be mocked:
class UserService {
@Mock()
getUser(id: number): User {
return {id, name: 'John'};
}
@Mock(() => ({id: 1, name: 'default'}))
createUser(): User {
return {id: 0, name: ''};
}
@Mock()
get userCount(): number {
return this.users.length;
}
@Mock()
async fetchUsers(): Promise<User[]> {
return [];
}
}
Mock Function Usage
import {mock} from '@corez/mock';
const simpleMock = mock.function();
const typedMock = mock.function<[name: string, age: number], boolean>();
const implMock = mock.function<[number], number>().mockImplementation(x => x * 2);
const asyncMock = mock.function<[id: number], Promise<User>>().mockResolvedValue({id: 1, name: 'John'});
const conditionalMock = mock.function<[number], string>().mockImplementation(x => (x > 0 ? 'positive' : 'negative'));
Controlling Return Values
const fn = mock.function();
fn.mockReturnValue(42);
console.log(fn());
fn.mockReturnValueOnce(1).mockReturnValueOnce(2).mockReturnValue(3);
console.log(fn());
console.log(fn());
console.log(fn());
fn.mockImplementation((x: number) => x * 2);
console.log(fn(21));
fn.mockClear();
fn.mockReset();
fn.mockRestore();
Advanced Features
Enhanced Mock Decorators
Mock.with
The Mock.with
decorator provides advanced mocking patterns:
class UserService {
@Mock.with({type: User, count: 3})
getUsers(): User[] {
return [];
}
@Mock.with({enum: UserStatus})
getUserStatus(): UserStatus {
return UserStatus.Active;
}
@Mock.with({pattern: /user_\d+/})
generateUserId(): string {
return '';
}
}
Mock.withFaker
The Mock.withFaker
decorator integrates with faker.js for realistic test data:
class UserService {
@Mock.withFaker(faker => ({
id: faker.number.int(),
name: faker.person.fullName(),
email: faker.internet.email(),
}))
generateUser(): User {
return new User();
}
@Mock.withFaker(faker => faker.date.future(), {
cached: true,
maxAge: 3600000,
})
getNextSchedule(): Date {
return new Date();
}
}
Instance Isolation
All mocked classes provide complete isolation between instances:
const service1 = new UserService();
const service2 = new UserService();
(service1.getUser as MockedFunction).mockReturnValue(user1);
(service2.getUser as MockedFunction).mockReturnValue(user2);
service1.getUser(1);
console.log((service1.getUser as MockedFunction).mock.calls.length);
console.log((service2.getUser as MockedFunction).mock.calls.length);
@Mock.withFaker(faker => faker.number.int(), {cached: true})
getRandomNumber(): number {
return 0;
}
const num1 = service1.getRandomNumber();
const num2 = service2.getRandomNumber();
Smart Caching System
The library includes a smart caching system with instance isolation and automatic cleanup:
class UserService {
@Mock.withFaker(faker => faker.number.int(), {cached: true})
getCachedNumber(): number {
return 0;
}
@Mock.withFaker(faker => faker.date.future(), {
cached: true,
maxAge: 3600000,
})
getExpiringDate(): Date {
return new Date();
}
}
Mock Patterns
The library supports various advanced mocking patterns:
Regular Expression Pattern
Generate strings matching a regex pattern:
class DataService {
@Mock.with(/\d{3}-\d{2}-\d{4}/)
getSocialSecurity(): string {
return '';
}
@Mock.with({pattern: /user_[a-z]{5}\d{3}/})
generateUserId(): string {
return '';
}
}
Smart Caching System
The library includes an advanced caching system with instance isolation:
class UserService {
@Mock.withFaker(faker => faker.number.int(), {
cached: true,
})
getCachedNumber(): number {
return 0;
}
@Mock.withFaker(faker => faker.date.future(), {
cached: true,
maxAge: 3600000,
})
get nextSchedule(): Date {
return new Date();
}
}
const service1 = new UserService();
const service2 = new UserService();
service1.getCachedNumber() === service1.getCachedNumber();
service1.getCachedNumber() !== service2.getCachedNumber();
Mock Function Behavior
The library provides detailed control over mock function behavior:
class UserService {
@Mock()
getUser(id: number): User {
return {id, name: 'original'};
}
}
const service = new UserService();
const mock = service.getUser as MockedFunction;
mock.mockImplementation(() => ({id: 1, name: 'first'}));
service.getUser(1);
mock.mock.calls.length === 1;
mock.mockImplementation(() => ({id: 2, name: 'second'}));
mock.mock.calls.length === 0;
mock.mockReset();
Instance Isolation
The library provides complete instance isolation for all mock types:
class UserService {
@Mock()
getUser(): User {
return {id: 0, name: ''};
}
@Mock()
get userCount(): number {
return 0;
}
@Mock.withFaker(faker => faker.number.int(), {cached: true})
getRandomNumber(): number {
return 0;
}
}
const service1 = new UserService();
const service2 = new UserService();
service1.getUser.mock !== service2.getUser.mock;
service1.userCount.mock !== service2.userCount.mock;
service1.getRandomNumber() === service1.getRandomNumber();
service1.getRandomNumber() !== service2.getRandomNumber();
const mock1 = service1.getUser as MockedFunction;
const mock2 = service2.getUser as MockedFunction;
mock1.mockReturnValue({id: 1, name: 'one'});
mock2.mockReturnValue({id: 2, name: 'two'});
service1.getUser().name === 'one';
service2.getUser().name === 'two';
Important Notes
-
Decorator Usage
- Decorators support both methods and accessors (getters/setters)
- Pattern-based mocks generate consistent values within instances
- Cached values are instance-specific
- Mock implementations are reset when changed
-
Caching Behavior
- Default cache size: 100 entries per instance
- Default cache expiry: 50ms
- Instance-specific caching
- Automatic cleanup of expired entries
- Cache keys include instance identity
-
Error Handling
- Detailed error messages with context
- Type-safe error handling
- Pattern validation for regex and enum patterns
- Implementation validation for class decorators
-
Type Safety
- Full TypeScript support
- Pattern-specific type checking
- Async function type safety
- Getter/setter type validation
- Instance-specific type checking
API Reference
For detailed API documentation, please visit our
API Documentation.
License
MIT
Contributing
Contributions are welcome! Please read our
Contributing Guidelines for details on how to submit pull
requests, report issues, and contribute to the project.