【问题标题】:Connection "default" was not found with NestJS Unit Testing of Service使用 NestJS 服务单元测试未找到连接“默认”
【发布时间】: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)

【问题讨论】:

    标签: typescript unit-testing mocking nestjs typeorm


    【解决方案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 中的函数链正常工作(它可能要复杂得多......)。再次感谢!
    • 哇,太好了。很高兴我能帮忙?
    猜你喜欢
    • 2021-08-04
    • 2020-02-13
    • 2019-11-22
    • 2019-09-08
    • 2018-09-22
    • 2020-12-03
    • 1970-01-01
    • 2020-05-09
    • 2020-03-22
    相关资源
    最近更新 更多