When working with NestJS modules that rely on TypeORM, it’s common to encounter challenges in testing without invoking the actual database. Instead of using a real database, we can mock the TypeORM repository to ensure isolated and faster unit tests. Let’s walk through how to achieve this using an ExampleModule
as our case study.
The ExampleModule
Implementation
Here’s the implementation of a basic ExampleModule
that has TypeORM dependencies:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ExampleEntity } from './entities/example.entity';
import { ExampleService } from './example.service';
import { ExampleController } from './example.controller';
@Module({
imports: [TypeOrmModule.forFeature([ExampleEntity])],
controllers: [ExampleController],
providers: [ExampleService],
})
export class ExampleModule {}
Key Points:
The
ExampleModule
imports theTypeOrmModule
and registers theExampleEntity
.It defines a service (
ExampleService
) and a controller (ExampleController
).
Why Mock TypeORM?
Directly connecting to a database during unit tests introduces dependencies that can:
Slow down test execution.
Cause flaky tests if the database state changes.
Make tests less predictable.
By mocking the TypeORM repository, we can:
Focus on testing the business logic.
Avoid any dependency on the database.
Achieve faster and more reliable tests.
Creating a Mock Module
We’ll create a mock module, MockExampleModule
, which excludes TypeORM imports to bypass database interactions.
import { Module } from '@nestjs/common';
import { ExampleService } from './example.service';
import { ExampleController } from './example.controller';
@Module({
imports: [],
controllers: [ExampleController],
providers: [ExampleService],
})
export class MockExampleModule {}
Key Points:
The
MockExampleModule
excludesTypeOrmModule
.It retains the controller and service for testing.
Writing Test Cases
Here’s how you can write unit tests for ExampleModule
:
example.module.spec.ts:
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ExampleEntity } from './entities/example.entity';
import { ExampleService } from './example.service';
import { ExampleController } from './example.controller';
import { CustomLogger } from '../utilities';
describe('ExampleModule', () => {
let service: ExampleService;
// Mock repository methods
const mockRepository = {
find: jest.fn(),
findOne: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ExampleController],
providers: [
ExampleService,
{
provide: getRepositoryToken(ExampleEntity), // Mock TypeORM repository
useValue: mockRepository,
},
CustomLogger,
],
})
.overrideModule(ExampleModule)
.useModule(MockExampleModule) // override Module with a Mock
.compile()
service = module.get<ExampleService>(ExampleService);
});
it('should be defined', () => {
expect(module).toBeDefined();
});
});
Key Points:
Mock Repository: Use
jest.fn()
to mock repository methods likefind
,findOne
,save
, anddelete
.Override Module: Use
overrideModule
to replace the actual module with a mock.MockExampleModule: Include the mock module to replace the original module’s imports and avoid TypeORM dependencies.
CustomLogger: Include your logger if necessary to ensure completeness
Conclusion
By following this approach, you can test your NestJS modules with TypeORM dependencies effectively without invoking the database. Mocking repositories ensures your tests are fast, reliable, and focused on the business logic. Start implementing this strategy in your projects to achieve robust and maintainable unit tests.
Top comments (0)