Search code examples
typescriptunit-testingmockingnestjstypeorm

Connection "default" was not found with NestJS Unit Testing of Service


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)

Solution

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

    Update:

    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.