简体   繁体   中英

Unit testing NestJS controller with injection

I need to make an unit test for a controller who use injection with NestJS.

I don't know how to mock and spy this service (MyEmitter). I need to declare it in test.controller.spec.ts inside beforeEach() but how?

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

Thank you really much for your help.

The easiest way to go about it, with the setup you currently have is to use module.get() like you already are for the controller and pass in the EVENT_EMITTER_TOKEN constant, then save that to a value declared in the describe block, just like how the let testController: TestController works. Something like this should suffice:

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

Rather than injecting every dependencies (which should be tested separately), it is better to use jest.spyOn because controller has a service dependency or dependencies which might have other dependencies.

We should mock the method that will be called in the current test.

Here is the sample controller test.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM