This is my BadRequestExceptionFilter written in Typescript for 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(),
});
}
}
This is my unit test andand 2 assert are done here. First assert is to check whether mockLogger.error called. It is working. Second assert to check whether response.code(status).send() is called. but getting this 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();
});
});
I think its straight forward to write the test case for exception filter using Nest Testing module. Check below how I achieved the same:
// 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'
});
});
});
});
I did something like this and it worked!
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()
As I understood , we always have to pass a mock for the "expect".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With