How to test custom Repository in Nestjs/TypeORM applications
Asked Answered
L

1

7

I am trying to add more testing codes to improve the quality of my sample codes.

Currently, I have a problem when testing UserRepository (not mock UserRepository), there are some custom methods I added in my custom UserRepository like this.

@EntityRepository(UserEntity)
export class UserRepository extends Repository<UserEntity> {
  findByEmail(email: string): Promise<UserEntity> {
    return this.findOne({ email: email });
  }
}

So I want to verify the findOne is called from the parent Repository.

I tried to add the following testing codes.

describe('UserRepository', () => {
  let local;
  let parentMock;

  beforeEach(() => {
    local = Object.getPrototypeOf(UserRepository);
    parentMock = {
      new: jest.fn(),
      construtor: jest.fn(),
      findOne: jest.fn(),
    };
    Object.setPrototypeOf(UserRepository, parentMock);
  });

  afterEach(() => {
    Object.setPrototypeOf(UserRepository, local);
  });

  it('should call findOne', async () => {
    const findByEmailSpy = jest.spyOn(parentMock, 'findOne');
    const users = new UserRepository();
    await users.findByEmail('[email protected]');
    expect(parentMock.mock.calls.length).toBe(1);
    expect(findByEmailSpy).toBeCalledWith({
      email: '[email protected]',
    });
  });
});

When running the tests, it complains there is no constructor() for new UserRepository().

Is there any way to fix this issue, or a better way to write these testing codes?

Labrie answered 18/5, 2021 at 5:1 Comment(0)
H
7

In order to properly test the user repository, findOne method has to be mocked.

import { Test, TestingModule } from '@nestjs/testing';
import { Repository } from 'typeorm';
import { UserEntity } from './user.entity';
import { UserRepository } from './user.repository';

describe('UserRepository', () => {
  let userRepository: UserRepository;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UserRepository],
    }).compile();

    userRepository = module.get<UserRepository>(UserRepository);
  });

  describe('findByEmail', () => {
    it('should return found user', async () => {
      const email = 'email';
      const user = {
        email,
      };
      const findOneSpy = jest
        .spyOn(userRepository, 'findOne')
        .mockResolvedValue(user as UserEntity);

      const foundUser = await userRepository.findByEmail(email);
      expect(foundUser).toEqual(user);
      expect(findOneSpy).toHaveBeenCalledWith(user);
    });
  });
});

Herisau answered 5/9, 2021 at 17:42 Comment(5)
If use EntityManager and QueryBuilder in the Repository, eg findByAuthor in the PostRepository, how to mock this?Labrie
for QueryBuilder, it can be mocked like the following: jest.spyOn(Repository.prototype, 'createQueryBuilder').mockReturnValue(SelectQueryBuilder.prototype); jest.spyOn(SelectQueryBuilder.prototype, 'where').mockReturnThis(); // the same goes for setParameter, skip and take methods jest.spyOn(SelectQueryBuilder.prototype, 'getMany').mockResolvedValue(data); Unload
not sure about mocking manager.findOne, what is the reason for using this.manager.find instead of this.find?Unload
I added a test make it work. But all readonly properties in the Repository should be injectable, but it does not work as expected like other injectable components.Labrie
Any ideas on how to do this when extending from AbstractRepository? I have created a separate question for it here.Comprehensive

© 2022 - 2024 — McMap. All rights reserved.