Mock TypeORM
Mock TypeORM is a TypeScript-first library designed to facilitate unit and integration testing of TypeORM-based applications by preventing actual database interactions. It is currently at version 1.0.4 and appears to be actively maintained, indicated by recent GitHub activity and a workflow badge. The package differentiates itself by utilizing Sinon.js for mocking, which makes it compatible with a wide range of Node.js testing frameworks such as Jest, Mocha, and Vitest, offering flexibility that some other mocking approaches (e.g., Jest-specific mocks) might lack. It provides a straightforward API to mock TypeORM's `DataSource`, `EntityManager`, `Transaction`, `QueryBuilder`, and `Repository` methods, enabling developers to control database responses programmatically without needing a real database connection. This approach significantly speeds up tests and makes them more deterministic and isolated, avoiding the complexities and flakiness associated with test databases.
Common errors
-
TypeError: (0, _mockTypeorm.MockTypeORM) is not a constructor
cause This usually happens in CommonJS environments trying to `require` an ESM-first package, or incorrect import syntax.fixFor TypeScript/ESM projects, use `import { MockTypeORM } from 'mock-typeorm';`. If forced to use CommonJS, ensure your build process correctly transpiles ESM or configure Node.js to handle ESM. Consider switching to ESM if possible. -
Error: Cannot find module 'sinon'
cause The `sinon` package is a peer dependency of `mock-typeorm` and must be installed explicitly.fixInstall Sinon.js and its TypeScript types: `npm install --save-dev sinon @types/sinon`. -
ConnectionNotFoundError: Connection "default" was not found.
cause `mock-typeorm` intercepts TypeORM calls, but if `TypeORM.initialize()` or a similar connection setup is still implicitly expected by parts of your application, this error can occur if the setup is not properly bypassed or mocked.fixEnsure that `mock-typeorm` is initialized and active before TypeORM connection-dependent code runs. Verify that no code attempts to establish a real database connection during unit tests, or that the `DataSource` itself is completely mocked to prevent this error. The primary method is to instantiate `new MockTypeORM()` before your tests.
Warnings
- gotcha `sinon` is a peer dependency and must be installed separately. Failing to install `sinon` (and its types `@types/sinon` for TypeScript projects) will result in runtime errors.
- gotcha It's crucial to reset the mock state between tests to ensure test isolation. Not doing so can lead to unexpected behavior where mock configurations from one test affect subsequent tests.
- breaking The package targets Node.js 18.x and above. Older Node.js versions might encounter compatibility issues, especially with ESM features, as the package is likely designed for modern JavaScript environments.
- gotcha When mocking TypeORM repositories, ensure all methods your service/controller interacts with are explicitly stubbed. If a method is called that hasn't been stubbed, it might lead to undefined behavior or errors, as `mock-typeorm` prevents actual DB calls.
Install
-
npm install mock-typeorm -
yarn add mock-typeorm -
pnpm add mock-typeorm
Imports
- MockTypeORM
const { MockTypeORM } = require('mock-typeorm');import { MockTypeORM } from 'mock-typeorm'; - Repository
import { Repository } from 'mock-typeorm';import { Repository } from 'typeorm';
Quickstart
import { DataSource, Repository, Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
import { MockTypeORM } from 'mock-typeorm';
import * as sinon from 'sinon';
@Entity()
class User {
@PrimaryGeneratedColumn()
id!: number;
@Column()
name!: string;
@Column()
email!: string;
}
// A simple service that uses the UserRepository
class UserService {
constructor(private userRepository: Repository<User>) {}
async createUser(name: string, email: string): Promise<User> {
const newUser = this.userRepository.create({ name, email });
return this.userRepository.save(newUser);
}
async findUserById(id: number): Promise<User | null> {
return this.userRepository.findOne({ where: { id } });
}
}
describe('UserService', () => {
let mockTypeORM: MockTypeORM;
let userRepository: Repository<User>;
let userService: UserService;
beforeEach(() => {
// Initialize MockTypeORM and reset Sinon stubs before each test
mockTypeORM = new MockTypeORM();
sinon.restore(); // Ensure stubs from previous tests are cleared
// Create a mock repository for the User entity
userRepository = mockTypeORM.onMock(User);
// Instantiate the service with the mocked repository
userService = new UserService(userRepository);
});
afterEach(() => {
// Restore TypeORM's original behavior and clear all mocks
mockTypeORM.restore();
});
it('should create a new user', async () => {
const userData = { name: 'Alice', email: 'alice@example.com' };
const expectedUser = { id: 1, ...userData };
// Stub the 'save' method of the mocked repository
sinon.stub(userRepository, 'save').resolves(expectedUser);
const result = await userService.createUser(userData.name, userData.email);
expect(result).toEqual(expectedUser);
expect(userRepository.save).toHaveBeenCalledWith(expect.objectContaining(userData));
});
it('should find a user by ID', async () => {
const expectedUser = { id: 1, name: 'Bob', email: 'bob@example.com' };
// Stub the 'findOne' method of the mocked repository
sinon.stub(userRepository, 'findOne').resolves(expectedUser);
const result = await userService.findUserById(1);
expect(result).toEqual(expectedUser);
expect(userRepository.findOne).toHaveBeenCalledWith({ where: { id: 1 } });
});
it('should return null if user not found', async () => {
// Stub the 'findOne' method to resolve with null
sinon.stub(userRepository, 'findOne').resolves(null);
const result = await userService.findUserById(99);
expect(result).toBeNull();
expect(userRepository.findOne).toHaveBeenCalledWith({ where: { id: 99 } });
});
});