Reputation: 115
I am currently unit testing my NestJS service. My entity is called 'User' and I established a basic Service that allows me to interact with a MS SQL server, with GET and POST endpoints established in my Controller.
While I was able to mock the Repository
that is used in the Service, I was unable to establish a mock getConnection
in a method that had to call getConection()
.
When I tried unit testing with npm run test:watch
, I get the error that ConnectionNotFoundError: Connection "default" was not found.
I have looked into (and in fact, taken much from) How to stub EntityManager and Connection in TypeORM with Jest, but this post does not seem to elaborate on a connection that was not established, which is my problem.
In any case, here's my service with the relevant parts:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { getConnection, Repository } from "typeorm";
import {User} from '../entities/user.entity';
@Injectable()
export class ServiceName {
constructor(@InjectRepository(User) private usersRepository: Repository<User>) {}
// Creating and inserting a new user into the database table using Repository
// unit testing for this one works fine
async createUserRepository(user: User): Promise<User> {
const newUser = this.usersRepository.create(user);
return await this.usersRepository.save(newUser);
}
// Creating and inserting a new user into the database table using QueryBuilder and getConnection
// unit testing for this one does not work so well
async createUserQueryBuilder(user: User): Promise<User> {
await getConnection()
.createQueryBuilder()
.insert()
.into(User)
.values([
user,
])
.execute();
return user;
}
And here's my spec.ts file for unit testing:
import { Test, TestingModule } from '@nestjs/testing';
import { ServiceName } from './app_codes_rms_area.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from '../entities/user.entity';
import { Connection, Repository } from 'typeorm';
describe('service tests', () => {
const repositoryMockFactory: () => MockType<Repository<any>> = jest.fn(() => ({
create: jest.fn(),
save: jest.fn(),
// other functions
}));
const mockConnectionFactory = jest.fn(() => ({
getConnection: jest.fn().mockReturnValue({
createQueryBuilder: jest.fn().mockReturnThis(),
getMany: jest.fn().mockReturnValue(allUsers),
insert: jest.fn().mockReturnThis(),
into: jest.fn().mockReturnThis(),
values: jest.fn().mockReturnThis(),
execute: jest.fn().mockReturnValue(user),
})
}));
let service: ServiceName;
let mockRepository: MockType<Repository<User>>;
let mockConnection: Connection;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ServiceName,
{
provide: getRepositoryToken(User), // a User Repository is injected to the service
useFactory: repositoryMockFactory, // using factory ensures that a new mock is used for every test
},
{
provide: Connection,
useFactory: mockConnectionFactory,
}
],
}).compile();
service = module.get<ServiceName>(ServiceName);
mockRepository = module.get(getRepositoryToken(User));
mockConnection = module.get<Connection>(Connection);
});
it('should create a new user and return it using Repository', async () => {
// some test that passes using mockRepository
});
it('should create a new user and return it using QueryBuilder (with mocked Connection)', async () => {
expect(await service.createUserQueryBuilder(user)).toEqual(user);
expect(mockConnection.createQueryBuilder).toBeCalled();
expect(mockConnection.createQueryBuilder()['insert']).toBeCalled();
expect(mockConnection.createQueryBuilder()['into']).toBeCalled();
expect(mockConnection.createQueryBuilder()['values']).toBeCalled();
expect(mockConnection.createQueryBuilder()['execute']).toBeCalled();
})
It is the second test, it('should create a new user and return it using QueryBuilder (with mocked Connection)'
, that triggers the following error:
● service tests › Service Functions › should create a new user and return it using QueryBuilder (with mocked Connection)
ConnectionNotFoundError: Connection "default" was not found.
at new ConnectionNotFoundError (error/ConnectionNotFoundError.ts:8:9)
at ConnectionManager.Object.<anonymous>.ConnectionManager.get (connection/ConnectionManager.ts:40:19)
at Object.getConnection (index.ts:252:35)
at ServiceName.createUserQueryBuilder (somefile:19:11)
at Object.it (somefile:134:34)
Upvotes: 3
Views: 2126
Reputation: 5814
The easiest solution would be to change your createUserQueryBuilder
method like below:
async createUserQueryBuilder(user: User): Promise<User> {
await this.usersRepository
.createQueryBuilder()
.insert()
.into(User)
.values([
user,
])
.execute();
return user;
}
Then you can update the repository mock repositoryMockFactory
:
const repositoryMockFactory: () => MockType<Repository<any>> = jest.fn(() => ({
create: jest.fn(),
save: jest.fn(),
createQueryBuilder: jest.fn().mockReturnThis(),
getMany: jest.fn().mockReturnValue(allUsers),
insert: jest.fn().mockReturnThis(),
into: jest.fn().mockReturnThis(),
values: jest.fn().mockReturnThis(),
execute: jest.fn().mockReturnValue(user),
}));
Based on your request, you can do something like this as well.
Update your spec.ts
file,
import * as typeorm from 'typeorm';
const getConnectionSpy = jest.spyOn(typeorm, 'getConnection');
getConnectionSpy.mockImplementation(() => ({
createQueryBuilder: jest.fn().mockReturnThis(),
getMany: jest.fn().mockReturnValue(allUsers),
insert: jest.fn().mockReturnThis(),
into: jest.fn().mockReturnThis(),
values: jest.fn().mockReturnThis(),
execute: jest.fn().mockReturnValue(user),
}));
Note that you will have to update your other typeorm
imports accordingly.
e.g.: typeorm.Connection
This is just for you to get an idea on how to do it. There might be syntax errors since I didn't test this.
Upvotes: 4