node.js - Prueba unitaria Jest para el método Catch del ExceptionFilter en Nodejs/NestJS
Este es mi FiltroBadRequestException escrito en Typescript para Nodejs/Nestjs
@Catch(BadRequestException)
export class BadRequestExceptionFilter implements ExceptionFilter {
constructor(private logger: AppLoggerService) {}
catch(exception: BadRequestException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status =
exception instanceof BadRequestException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message = {
Title: exception.message.error,
Type: 'Exception - BadRequestExceptionFilter',
Detail: exception.message,
Status: '',
};
this.logger.error(message, '');
response.code(status).send({
statusCode: status,
...(exception.getResponse() as object),
timestamp: 'Exception - BadRequestException' + new Date().toISOString(),
});
}
}
Esta es mi prueba unitaria y se han realizado 2 afirmaciones aquí. La primera afirmación es para verificar si mockLogger.error fue llamado. Está funcionando. La segunda afirmación es para verificar si response.code(status).send() fue llamado, pero estoy obteniendo este error. expect(jest.fn()).toBeCalled()
Expected number of calls: >= 1
Received number of calls: 0
const mockLogger = { error: jest.fn() };
const mockContext: any = {
switchToHttp: () => ({
getRequest: () => ({
url: 'mock-url',
}),
getResponse: () => {
const response = {
code: jest.fn().mockReturnThis(),
send: jest.fn().mockReturnThis(),
};
return response;
},
}),
};
describe('BadRequestExceptionFilter', () => {
let filter: BadRequestExceptionFilter;
beforeEach(() => {
filter = new BadRequestExceptionFilter(mockLogger as any);
});
it('should catch and log the error', () => {
const mockException: BadRequestException = new BadRequestException();
mockException.name = 'BadRequestException';
mockException.getResponse = jest.fn().mockReturnValue(of('getResponse'));
mockException.getStatus = () => 404;
jest.fn(mockContext.switchToHttp().getResponse().send);
filter.catch(mockException, mockContext);
expect(mockLogger.error).toBeCalled();
expect(
mockContext
.switchToHttp()
.getResponse()
.code().send,
).toBeCalled();
});
});
Respuesta #1
Creo que es directo escribir el caso de prueba para el filtro de excepciones usando el módulo de pruebas de Nest. Mira abajo cómo logré lo mismo:
// all-exception.filter.spec.ts
import {
Test,
TestingModule
} from '@nestjs/testing';
import {
HttpStatus,
HttpException
} from '@nestjs/common';
import { AllExceptionsFilter } from './all-exceptions.filter';
import { AppLoggerService } from '../services/logger/app-logger.service';
const mockAppLoggerService = {
info: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
debug: jest.fn()
};
const mockJson = jest.fn();
const mockStatus = jest.fn().mockImplementation(() => ({
json: mockJson
}));
const mockGetResponse = jest.fn().mockImplementation(() => ({
status: mockStatus
}));
const mockHttpArgumentsHost = jest.fn().mockImplementation(() => ({
getResponse: mockGetResponse,
getRequest: jest.fn()
}));
const mockArgumentsHost = {
switchToHttp: mockHttpArgumentsHost,
getArgByIndex: jest.fn(),
getArgs: jest.fn(),
getType: jest.fn(),
switchToRpc: jest.fn(),
switchToWs: jest.fn()
};
describe('System header validation service', () => {
let service: AllExceptionsFilter;
beforeEach(async () => {
jest.clearAllMocks();
const module: TestingModule = await Test.createTestingModule({
providers: [
AllExceptionsFilter,
{
provide: AppLoggerService,
useValue: mockAppLoggerService
},
]
}).compile();
service = module.get<AllExceptionsFilter>(AllExceptionsFilter);
});
describe('All exception filter tests', () => {
it('should be defined', () => {
expect(service).toBeDefined();
});
it('Http exception', () => {
service.catch(
new HttpException('Http exception', HttpStatus.BAD_REQUEST),
mockArgumentsHost
);
expect(mockHttpArgumentsHost).toBeCalledTimes(1);
expect(mockHttpArgumentsHost).toBeCalledWith();
expect(mockGetResponse).toBeCalledTimes(1);
expect(mockGetResponse).toBeCalledWith();
expect(mockStatus).toBeCalledTimes(1);
expect(mockStatus).toBeCalledWith(HttpStatus.BAD_REQUEST);
expect(mockJson).toBeCalledTimes(1);
expect(mockJson).toBeCalledWith({
message: 'Http exception'
});
});
});
});
Respuesta #2
¡Hice algo así y funcionó!
export const contextMock = (roles?: string[]) => {
const ctx: any = {}
ctx.switchToHttp = jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(requestMock()),
getResponse: jest.fn().mockReturnValue(responseMock()),
})
ctx.getHandler = jest.fn().mockReturnValue({ roles }) as Function
return ctx
}
-----------------------------------------------
const ctxMock = contextMock() as any
expect(ctxMock.switchToHttp).toHaveBeenCalled()
expect(ctxMock.switchToHttp().getResponse).toHaveBeenCalled()
expect(ctxMock.switchToHttp().getResponse().status).toHaveBeenCalled()
expect(ctxMock.switchToHttp().getResponse().json).toHaveBeenCalled()
Según entendí, siempre tenemos que pasar un mock para el "expect".
