ts-mockito
ts-mockito is a comprehensive mocking library for TypeScript, drawing inspiration from the well-known Java Mockito.org framework. Currently stable at version 2.6.1, it provides strongly typed mocks that enhance unit testing with features such as automatic IDE autocompletion and clear, readable error messages. The library allows for mocking classes, abstract classes, and interfaces, spying on real objects, and offers extensive stubbing capabilities including returning values, throwing errors, resolving/rejecting promises, and executing custom functions. ts-mockito maintains a moderate release cadence, with several updates annually focusing on new features and bug fixes. Its key differentiators include robust TypeScript integration, advanced verification options like call count, order, and argument capturing, and recent enhancements like improved type checking for the `deepEqual` matcher and multi-mock resetting.
Common errors
-
Maximum call stack size exceeded
cause Specific scenarios involving deeply nested calls or complex mocking setups could trigger a stack overflow in earlier versions.fixThis issue was fixed in v2.2.9. Ensure you are using ts-mockito version 2.2.9 or newer. -
Type 'Foo' is not assignable to type 'Partial<Foo>'. Property 'bar' is missing in type 'Partial<Foo>' but required in type 'Foo'.
cause A common type error when using the `deepEqual` matcher after v2.5.0 if the expected value's type does not perfectly align with the actual argument's type, especially when the expected type has more required fields.fixCast the expected argument to `Partial<YourType>` or `any` if a partial match is intended, e.g., `verify(mockedService.method(deepEqual(expectedParams as Partial<ServiceType>))).called();` -
Error: Expected "methodName(arg)" to be called X time(s). But has been called Y time(s).
cause This is a runtime error from ts-mockito's `verify` function indicating a mismatch between the expected number of calls for a specific method/argument combination and the actual calls made to the mock.fixReview your test logic to ensure the mock method is being called the expected number of times or adjust your `verify` expectation (e.g., `once()`, `twice()`, `times(count)`, `atLeast(count)`). -
TypeError: Cannot read properties of undefined (reading '__ts_mockito_args')
cause Can occur if `instance()` is not called to retrieve the mockable object before passing it to the code under test, or if the `mock()` function is passed an invalid argument.fixAlways ensure you call `instance(mockedObject)` to get the actual mocked object to interact with. Double-check that `mock()` is receiving a valid class or abstract class.
Warnings
- breaking The `deepEqual` matcher received significant type checking improvements in v2.5.0. This can cause existing code to fail compilation if provided arguments do not perfectly match the expected type, for example, if the expected parameter has more fields than the provided one. Use `Partial<T>` for easy migration if you intend to ignore extra fields.
- breaking Upgrading from ts-mockito 1.x to 2.x involves significant changes in API usage. Consult the official 1.x to 2.x migration guide on GitHub to understand the required updates for your codebase.
- gotcha Stubbing property values that do not have explicit getters (i.e., just direct properties) only works correctly in environments that support Proxy objects (ES6 and later). In older JavaScript environments without Proxy support, this feature will not function as expected.
- gotcha Support for mocking interfaces was introduced in v2.4.0. To mock an interface, you must use a generic type with the `mock` function and omit its argument: `let mockedInterface: MyInterface = mock<MyInterface>();` Incorrect syntax will lead to runtime errors or compilation issues.
Install
-
npm install ts-mockito -
yarn add ts-mockito -
pnpm add ts-mockito
Imports
- mock
const { mock } = require('ts-mockito');import { mock } from 'ts-mockito'; - instance
import instance from 'ts-mockito';
import { instance } from 'ts-mockito'; - when
const when = require('ts-mockito').when;import { when } from 'ts-mockito'; - verify
import { Verify } from 'ts-mockito';import { verify } from 'ts-mockito'; - deepEqual
import { deepEqual } from 'ts-mockito';
Quickstart
import { mock, instance, when, verify, anyNumber } from 'ts-mockito';
class FooService {
getBar(value: number): string {
throw new Error('Should not be called directly');
}
async getAsyncData(id: string): Promise<string> {
return Promise.resolve(`Data for ${id}`);
}
get sampleGetter(): string {
return 'realValue';
}
}
// Create a mock instance of FooService
const mockedFooService: FooService = mock(FooService);
// Stub a method call to return a specific value
when(mockedFooService.getBar(3)).thenReturn('three');
when(mockedFooService.getBar(anyNumber())).thenReturn('anyNumberResult');
// Stub a getter property
when(mockedFooService.sampleGetter).thenReturn('mockedGetterValue');
// Stub an async method to resolve a promise
when(mockedFooService.getAsyncData('testId')).thenResolve('resolvedData');
// Get the mock instance to use in your code
const fooService: FooService = instance(mockedFooService);
// Use the mocked instance
console.log('Call with 3:', fooService.getBar(3)); // Should print 'three'
console.log('Call with 5:', fooService.getBar(5)); // Should print 'anyNumberResult'
console.log('Getter value:', fooService.sampleGetter); // Should print 'mockedGetterValue'
fooService.getAsyncData('testId').then(data => {
console.log('Async data:', data); // Should print 'resolvedData'
});
// Verify that methods were called as expected
verify(mockedFooService.getBar(3)).once();
verify(mockedFooService.getBar(anyNumber())).thrice(); // 3, 5, and the `anyNumber()` for the `5` call
verify(mockedFooService.getAsyncData('testId')).called();
// Optionally, verify that an unexpected call was NOT made (e.g., if you only stubbed 'testId')
// verify(mockedFooService.getAsyncData('otherId')).never();