Reputation: 75
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.
Upvotes: 3
Views: 16613
Reputation: 945
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);
});
});
});
Upvotes: 0
Reputation: 70111
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);
});
});
});
Upvotes: 8