'How Do You Mock EntityManger In TypeORM?

I have the following code to mock entityManager from my NestJS service, it works to some extent but the issue comes with specifying what the findOne should return for different entity.

    {
      provide: getEntityManagerToken(),
      useFactory: (connection: Connection) => {
        const entityManager = {
          getRepository: jest.fn(() => {
            return {
              findOne: jest.fn().mockResolvedValue({}),
              save: jest.fn().mockResolvedValue(carData),
              createQueryBuilder: jest.fn(() => ({
                select: jest.fn().mockReturnThis(),
                from: jest.fn().mockReturnThis(),
                leftJoinAndSelect: jest.fn().mockReturnThis(),
                where: jest.fn().mockReturnThis(),
                addGroupBy: jest.fn().mockReturnThis(),
                groupBy: jest.fn().mockReturnThis(),
                getOne: jest.fn().mockResolvedValue(''),
              })),
            };
          }),
        };
        return entityManager;
      },
    },

My Service looks like the below code:

export class CarService {
  constructor(
    @InjectEntityManager()
    private readonly entityManager: EntityManager,
  ) {
    this.carRepository = entityManager.getRepository(Car);
    this.modelRepository = entityManager.getRepository(Model);
   }
    
      const car = await this.carRepository.findOne(data.id);

      const model = await this.modelRepository.findOne(data.modelId);
 

How do I mock the findOne result from this.carRepository to be different from the findOne result in this.modelRepository considering that they both will be called within the same service.



Solution 1:[1]

We have the possibility of using the EntityManager API to create transactions with several repositories that can execute a rollback automatically if any of these operations fail, and in the tests we can use the Singleton pattern to define a mock of this entity that allows us to return the same instance of these repositories to test that all these read and write operations have been performed as expected. e.g

import { createMock } from '@golevelup/ts-jest';
import { Test, TestingModule } from '@nestjs/testing';
import { EntityManager } from 'typeorm';

describe('MyService', () => {
  let service: MyService;
  let entityManager: EntityManager;
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        MyService,
        { provide: EntityManager, useValue: createMock<EntityManager>() },
      ],
    }).compile();

    service = module.get(CatalogsService);
    entityManager = module.get(EntityManager);
  });
  
  it('should call a transaction correctly', async () => {
    const mockEntityManager = {
      save: jest.fn(),
      getCustomRepository: jest.fn((fn) => mockEntityManager[fn] || (mockEntityManager[fn] = createMock<typeof fn>())),
    };
    const spyTransaction = (entityManager.transaction as jest.Mock).mockImplementation((cb) => cb(mockEntityManager));
    const firstRepo: FirstRepository = mockEntityManager.getCustomRepository(SubCatalogRepository);
    const secondRepo: SecondRepository = mockEntityManager.getCustomRepository(SecondRepository);
    await service.saveSomething('MOCK DATA');

    expect(spyTransaction).toHaveBeenCalled();
    expect(firstRepo.save).toHaveBeenCalled();
    expect(secondRepo.save).toHaveBeenCalled();
    expect(mockEntityManager.save).toHaveBeenCalled();
  });
});

As we can see, it's very easy to mock these external services such as the TypeORM EntityManager, etc. using the createMock function, which automatically injects Jest mock functions to replace external implementations on which our code depends, which in a unit test should not matter.

Happy coding! <3

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jdnichollsc