unit-testingautomated-testsnestjsnestjs-testing

how to provide injectables to service when Testing with NestJs Test.createTestingModule function?


I am trying to set up my test suite for a specific service which takes in a repository and two other services at the constructor parameter level injection using Nest's built-in Dependency Injection (DI). The application is working fine no issue there. But when testing it returns:

Nest can't resolve dependencies of the AdjustmentService (?, RetailAdjustmentService, AdjustmentFileService). Please make sure that the argument AdjustmentRepository at index [0] is available in the RootTestModule context.

i know there must be something i am doing wrong but the docs were not of much help regarding that.

here is the constructor of that specific service i am testing :

export class AdjustmentService {
  constructor(
    private readonly adjustmentsRepository: AdjustmentRepository,
    private readonly _retailAdjustmentService: RetailAdjustmentService,
    private readonly adjustmentFilesService: AdjustmentFileService
  ) {}
//service functions here...
}

and here is my test

import { AdjustmentFileService } from "@/services/adjustmentFile.service";
import { Test, TestingModule } from "@nestjs/testing";
import { AdjustmentService } from "./adjustment.service";
import { Repository } from "typeorm";
import { AdjustmentEntity } from "@/entities/adjustment.entity";
import { getRepositoryToken } from "@nestjs/typeorm";
import { RetailAdjustmentService } from "@/services/retail/retailAdjustment.service";

describe("Adjustment Service", () => {
  let adjustmentService: AdjustmentService;
  let adjustementRepository: Repository<AdjustmentEntity>;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      providers: [
        AdjustmentService,
        AdjustmentFileService,
        RetailAdjustmentService,
        {
          provide: getRepositoryToken(AdjustmentEntity),
          useValue: {
            save: jest.fn(),
            createQueryBuilder: jest.fn(),
            delete: jest.fn(),
            where: jest.fn(),
            execute: jest.fn(),
          },
        },
      ],
    }).compile();

    adjustmentService = module.get<AdjustmentService>(AdjustmentService);
    adjustementRepository = module.get<Repository<AdjustmentEntity>>(
      getRepositoryToken(AdjustmentEntity)
    );
  });

  describe("updateAdjustment", () => {
    it("should update the adjustment", async () => {
      const repoSpy = jest
        .spyOn(adjustementRepository, "save")
        .mockResolvedValue({ id: 1 } as AdjustmentEntity);

      const adjToUpdate = new AdjustmentEntity();
      adjToUpdate.id = 1;
      adjToUpdate.pilars = "RT";
      adjToUpdate.amount = 15000;

      expect(
        adjustmentService.updateAdjustment(1, adjToUpdate)
      ).resolves.toEqual({ id: 1 });

      expect(repoSpy).toBeCalledWith(adjToUpdate);
    });
  });

  it("should return a list of adjustments", async () => {});
});


Solution

  • If you are wanting to create mocks of these services, which you should in unit tests, then you should make custom providers that have a provide property of the class you want to mock and a useValue property of the mock to use. For example, AdjustmentFileService's mock might look something like

    {
      provide: AdjustmentFileService,
      useValue: {
        upload: jest.fn()
      }
    }
    

    You're on the right track with the getRepositoryToken approach, but as your AdjustmentRepository is a class you directly inject, you need to use AdjustmentRepository as the provide value as well.

    This repository has a lot of testing examples and might serve as a good reference.