使用 NestJS 服务单元测试未找到连接“默认”

Posted

技术标签:

【中文标题】使用 NestJS 服务单元测试未找到连接“默认”【英文标题】:Connection "default" was not found with NestJS Unit Testing of Service 【发布时间】:2021-08-03 21:28:18 【问题描述】:

我目前正在对我的 NestJS 服务进行单元测试。我的实体称为“用户”,我建立了一个基本服务,允许我与 MS SQL 服务器交互,并在我的控制器中建立 GET 和 POST 端点。

虽然我能够模拟服务中使用的 Repository,但我无法在必须调用 getConection() 的方法中建立模拟 getConnection

当我尝试使用 npm run test:watch 进行单元测试时,我收到了 ConnectionNotFoundError: Connection "default" was not found. 的错误,我已经研究过(实际上,从How to stub EntityManager and Connection in TypeORM with Jest 中提取了很多内容),但这篇文章似乎没有详细说明没有成立,这是我的问题。

无论如何,这是我的相关部分的服务:

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;  
  

这是我用于单元测试的 spec.ts 文件:

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('should create a new user and return it using QueryBuilder (with mocked Connection)'

  ● 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)

【问题讨论】:

【参考方案1】:

最简单的解决方案是更改您的 createUserQueryBuilder 方法,如下所示:

async createUserQueryBuilder(user: User): Promise<User> 
  await this.usersRepository
    .createQueryBuilder()
    .insert()
    .into(User)
    .values([
        user, 
     ])
    .execute();
  return user;  

然后你可以更新存储库模拟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),
));

更新:

根据您的要求,您也可以这样做。

更新您的 spec.ts 文件,

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),
));

请注意,您必须相应地更新您的其他 typeorm 导入。 例如:typeorm.Connection

这只是为了让您了解如何操作。由于我没有对此进行测试,因此可能存在语法错误。

【讨论】:

感谢您的回答!确实,这似乎有效;但我真的很想坚持使用getConnection()。 (真的,这是为了练习;我的新角色涉及 NestJS)。您对模拟 getConnection 有什么建议吗?我找到了一种在我的服务中使用@InjectConnection() private connection: Connection 并将其与我的spec.ts 文件中的provide: Connection, useFactory: mockConnectionFactory, 结合起来的方法(并且它可以工作),但在这里我模拟的是注入连接,而不是导入的getConnection .如果我可以模拟导入的 getConnection,那就太好了。 我更新了我的答案,但我不能保证它会起作用。祝你的新角色好运! ? 嗯,还是不行;但感谢您的回答!我觉得我需要在这里和那里做一些调整,也许...... 编辑:我将您的方法调整为一个更简单的示例,其中我使用 getManager() 后跟 delete 单个函数并且它有效!我很确定需要进行一些进一步的调整才能使createQueryBuilder 中的函数链正常工作(它可能要复杂得多......)。再次感谢! 哇,太好了。很高兴我能帮忙?

以上是关于使用 NestJS 服务单元测试未找到连接“默认”的主要内容,如果未能解决你的问题,请参考以下文章

未找到连接“默认”-TypeORM、NestJS 和外部 NPM 包

使用带有 TypeOrm 的 NestJS 连接 MySQL 数据库时出现问题

使用注入对 NestJS 控制器进行单元测试

Nestjs 单元测试 - 模拟方法守卫

NestJS如何在单元测试中创建新的猫鼬模型?

无法使用 nestjs/mongoose 连接 mongoDB