A powerful and flexible TypeScript mocking library for testing
npm install @corez/mockerA powerful TypeScript mocking library with flexible configuration and behavior control.



- ๐ Fully type-safe API with TypeScript
- ๐ญ Function, object and class mocking
- ๐ช Flexible mock configuration
- ๐ฒ Call tracking and verification
- ๐พ Instance isolation and state management
- ๐ฏ Getter/Setter mocking support
- ๐ Original implementation call-through
- ๐ฆ Zero runtime dependencies
- ๐ Comprehensive error handling
- ๐งช Async function support
- ๐จ Deep object comparison
- ๐ง Chainable API
- Installation
- Quick Start
- Basic Usage
- Function Mocking
- Object Mocking
- Class Mocking
- Instance Mocking
- Advanced Usage
- Call Through Behavior
- Keeping Original Methods
- Assertions
- Basic Assertions
- Argument Matching
- Return Value Assertions
- Call Order Assertions
- Error Assertions
- Context Assertions
- Deep Equality
- Best Practices
- Contributing
- License
``bashUsing pnpm (recommended)
pnpm add -D @corez/mocker
$3
- Node.js 16.0.0 or later
- TypeScript 5.0.0 or later
Quick Start
`typescript
import {mocker} from '@corez/mocker';// Mock a simple function
const fn = mocker.fn();
fn.mockReturnValue(42);
console.log(fn()); // 42
// Mock an object with call tracking
const user = {getName: () => 'John'};
const mockedUser = mocker.object(user);
mockedUser.getName.mockReturnValue('Jane');
console.log(mockedUser.getName()); // Jane
console.log(mockedUser.getName.mock.calls.length); // 1
// Mock a class with instance tracking
class UserService {
getUser(id: number) {
return {id, name: 'John'};
}
}
const MockedService = mocker.class(UserService);
const instance = new MockedService();
instance.getUser.mockReturnValue({id: 1, name: 'Jane'});
console.log(MockedService.mock.instances.length); // 1
`Basic Usage
$3
`typescript
// Basic mock function
const fn = mocker.fn();// With implementation
const fn = mocker.fn((x: number) => x * 2);
// Return values
fn.mockReturnValue(42);
fn.mockReturnValueOnce(1);
fn.mockReturnValueOnce(2).mockReturnValue(3);
// Implementations
fn.mockImplementation(x => x * 2);
fn.mockImplementationOnce(x => x + 1);
// Async functions
const asyncFn = mocker.fn(async () => 'result');
asyncFn.mockResolvedValue('mocked');
asyncFn.mockRejectedValue(new Error('failed'));
// Reset and verification
fn.mockClear(); // Clear calls
fn.mockReset(); // Clear calls and implementation
`$3
`typescript
interface User {
id: number;
name: string;
getFullName(): string;
}const user: User = {
id: 1,
name: 'John',
getFullName: () => 'John Doe',
};
// Mock with type safety
const mockedUser = mocker.object(user);
// Mock methods
mockedUser.getFullName.mockReturnValue('Jane Doe');
// Call through to original implementation
const mockedWithCallThrough = mocker.object(user, {callThrough: true});
console.log(mockedWithCallThrough.getFullName()); // 'John Doe'
// Reset mocks
mockedUser.mockReset();
`$3
`typescript
class UserService {
private api: ApiClient; constructor(api: ApiClient) {
this.api = api;
}
async getUser(id: number): Promise {
return this.api.fetch(
/users/${id});
}
}// Mock entire class
const MockedService = mocker.class(UserService);
const instance = new MockedService();
// Mock specific method
instance.getUser.mockResolvedValue({id: 1, name: 'John'});
// Custom implementation
MockedService.mockImplementation(
class extends UserService {
async getUser(id: number) {
return {id, name: 'Custom'};
}
},
);
// Reset class
MockedService.mockReset();
`$3
`typescript
class User {
constructor(public name: string) {}
getName() {
return this.name;
}
}// Create mocked instance with overrides
const mockedUser = mocker.instance(User, {
name: 'Jane',
getName: () => 'Jane Doe',
});
// Mock existing instance
const user = new User('John');
const mockedExisting = mocker.instance(user);
`Advanced Usage
$3
`typescript
class Calculator {
add(a: number, b: number) {
return a + b;
}
}// Mock with call through
const mockedCalc = mocker.object(new Calculator(), {
callThrough: true,
});
// Calls original implementation
console.log(mockedCalc.add(2, 3)); // 5
// Still tracks calls
console.log(mockedCalc.add.mock.calls); // [[2, 3]]
`$3
`typescript
const obj = {
method1() {
return 1;
},
method2() {
return 2;
},
};// Keep specific methods original
const mocked = mocker.object(obj, {
keepOriginal: ({property}) => property === 'method1',
});
// method1 remains original, method2 is mocked
console.log(mocked.method1()); // 1
console.log(mocked.method2()); // undefined
`Assertions
@corez/mocker provides a powerful assertion API with various matching patterns and detailed error reporting.
$3
`typescript
// Call count assertions
assert.called(spy); // Called at least once
assert.notCalled(spy); // Never called
assert.calledOnce(spy); // Called exactly once
assert.calledTwice(spy); // Called exactly twice
assert.calledThrice(spy); // Called exactly three times
assert.callCount(spy, n); // Called n times// Argument assertions
assert.calledWith(spy, ...args); // Called with matching arguments
assert.calledWithExactly(spy, ...args); // Called with exact arguments
assert.calledOnceWith(spy, ...args); // Called once with matching arguments
`$3
@corez/mocker supports three types of matchers:
1. Built-in Matchers
`typescript
import {matchers} from '@corez/mocker';// Type matching
assert.calledWith(
spy,
matchers.string(), // Match any string
matchers.number(), // Match any number
matchers.boolean(), // Match any boolean
matchers.any(), // Match any value
);
// Array matching
assert.calledWith(
spy,
matchers.arrayContaining([1, 2]), // Array contains specified elements
);
// Object matching
assert.calledWith(
spy,
matchers.objectContaining({
// Object contains specified properties
id: matchers.number(),
name: matchers.string(),
}),
);
`2. Jest-style Matchers
`typescript
assert.calledWith(spy,
expect.any(Number), // Type matching
expect.stringMatching(/test/), // RegExp matching
expect.objectContaining({...}), // Object containing
expect.arrayContaining([...]) // Array containing
);
`3. Custom Matchers
`typescript
assert.calledWith(spy, {
asymmetricMatch: actual => actual.startsWith('test'),
});
`$3
Support for nested objects and arrays deep matching:
`typescript
assert.calledWith(spy, {
user: {
id: matchers.number(),
profile: {
name: matchers.string(),
tags: matchers.arrayContaining(['admin']),
meta: matchers.objectContaining({
verified: matchers.boolean(),
}),
},
},
});
`$3
Multiple error matching patterns:
`typescript
// String matching
assert.threw(spy, 'error message');// RegExp matching
assert.threw(spy, /error/);
// Constructor matching
assert.threw(spy, TypeError);
// Property matching
assert.threw(spy, {
message: 'error message',
code: 'ERR_001',
});
// Custom matching
assert.threw(spy, {
asymmetricMatch: error => error.code === 'ERR_001',
});
`$3
`typescript
// this context assertions
assert.calledOn(spy, thisValue);// Constructor call assertions
assert.calledWithNew(spy);
`$3
When assertions fail, detailed error reports are generated:
`typescript
// Call: spy({name: 'test', age: 25})
assert.calledWith(spy, {
name: 'other',
age: 30,
});// Error message:
// AssertionError: expected spy to have been called with specific arguments
// Call 1:
// Expected: [{"name":"other","age":30}]
// Received: [{"name":"test","age":25}]
//
// Difference at index 0:
// Expected: {"name":"other","age":30}
// Received: {"name":"test","age":25}
`Best Practices
1. Reset Mocks Between Tests
`typescript
beforeEach(() => {
mocker.reset(mockedFunction);
mockedObject.mockReset();
MockedClass.mockReset();
});
`2. Type Safety
`typescript
// Use type parameters for better type inference
const fn = mocker.fn<(x: number) => string>();
const obj = mocker.object(service);
`3. Avoid Over-mocking
`typescript
// Bad: Mocking everything
const mockedService = mocker.object(service);// Good: Mock only what you need
const mockedService = mocker.object(service, {
keepOriginal: ['helper1', 'helper2'],
});
`4. Use Call Through When Needed
`typescript
// Only mock what needs to be mocked
const mockedService = mocker.object(service, {
callThrough: true,
keepOriginal: ['util1', 'util2'],
});
``Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull
requests to us.
This project is licensed under the Apache-2.0 License - see the LICENSE file for details.