typescript - Enhetstestning av NestJS-kontroller med injektion


Jag behöver göra ett enhetstest för en controller som använder injection med NestJS.

Jag vet inte hur man mockar och spionerar på den här tjänsten (MyEmitter). Jag behöver deklarera den i test.controller.spec.ts inuti beforeEach(), men hur?

test.controller.ts

import {
  Controller,
  Body,
  Post,
} from '@nestjs/common';
import {
  WebhookDto,
} from './dto/webhook.dto';
import { MyEmitter } from './test.events';
import { InjectEventEmitter } from 'nest-emitter';

@Controller()
export class TestController {
  constructor(
    @InjectEventEmitter() private readonly myEmitter: MyEmitter,
  ) {}

  @Post('webhook')
  public async postWebhook(
    @Body() webhookDto: WebhookDto,
  ): Promise<void> {
    ...
    this.myEmitter.emit('webhook', webhookDto);
  }
}

test.controller.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { TestController } from './test.controller';
import EventEmitter = require('events');
import { EVENT_EMITTER_TOKEN } from 'nest-emitter';
import { MyEmitter } from './test.events';

describe('Test Controller', () => {
  let testController: TestController;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [],
      providers: [
        {
          provide: EVENT_EMITTER_TOKEN,
          useValue: {
            emit: jest.fn(),
          },
        },
      ],
      controllers: [TestController],
    }).compile();

    testController = module.get<TestController>(TetsController);
  });

  describe('postWebhook', () => {
    it('should send the event', async () => {
      const myEmitterSpy = jest.spyOn(myEmitter, 'emit');
      const result = await testController.postWebhook({...});
      expect(myEmitterSpy).toBeCalledTimes(1);
    });
  });
});

Tack så mycket för din hjälp.

Svar #1

Det enklaste sättet att göra det på, med den konfiguration du har just nu, är att använda module.get() precis som du redan gör för controllern och skicka in konstanten EVENT_EMITTER_TOKEN, sedan spara det till ett värde deklarerat i describe-blocket, precis som hur let testController: TestController fungerar. Något som detta borde räcka:

import { Test, TestingModule } from '@nestjs/testing';
import { TestController } from './test.controller';
import EventEmitter = require('events');
import { EVENT_EMITTER_TOKEN } from 'nest-emitter';
import { MyEmitter } from './test.events';

describe('Test Controller', () => {
  let testController: TestController;
  let myEmitter: MyEmitter;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [],
      providers: [
        {
          provide: EVENT_EMITTER_TOKEN,
          useValue: {
            emit: jest.fn(),
          },
        },
      ],
      controllers: [TestController],
    }).compile();

    testController = module.get<TestController>(TetsController);
    myEmitter = module.get<MyEmitter>(EVENT_EMITTER_TOKEN);
  });

  describe('postWebhook', () => {
    it('should send the event', async () => {
      const myEmitterSpy = jest.spyOn(myEmitter, 'emit'); // you can also add on mockResponse type functions here like mockReturnValue and mockResolvedValue
      const result = await testController.postWebhook({...});
      expect(myEmitterSpy).toBeCalledTimes(1);
    });
  });
});

Svar #2

Istället för att injicera alla beroenden (som bör testas separat), är det bättre att använda jest.spyOn eftersom controllern har ett tjänstberoende eller beroenden som kan ha andra beroenden.

Vi bör mocka metoden som kommer att anropas i det aktuella testet.

Här är exempelkontrolltestet.

import { SampleController } from './sample.controller';
import { SampleService } from './sample.service';

describe('SampleController', () => {
  let sampleController: SampleController;
  let sampleService: SampleService;

  beforeEach(() => {
    // SampleService depends on a repository class
    // Passing null becasue SampleService will be mocked
    // So it does not need any dependencies.
    sampleService = new SampleService(null);
    // SampleController has one dependency SampleService
    sampleController = new SampleController(sampleService);
  });

  it('should be defined', async () => {
    expect(sampleController).toBeDefined();
  });

  describe('findAll', () => {
    it('should return array of samples', async () => {
      // Response of findAllByQuery Method
      // findAllByQUeryParams is a method of SampleService class.
      // I want the method to return an array containing 'test' value'.
      const expectedResult = ['test'];

      // Creating the mock method
      // The method structure is the same as the actual method structure.
      const findAllByQueryParamsMock = async (query: any) => expectedResult;

      // I am telling jest to spy on the findAllByQueryParams method
      // and run the mock method when the findAllByQueryParams method is called in the controller.
      jest
        .spyOn(sampleService, 'findAllByQueryParams')
        .mockImplementation(findAllByQueryParamsMock);

      const actualResult = await sampleController.findAll({});

      expect(actualResult).toBe(expectedResult);
    });
  });
});

Relaterade artiklar