Nodejs/NestJS ExceptionFilter Catch 方法的 Jest 单元测试



这是我用 Typescript for Nodejs/Nestjs 编写的 BadRequestExceptionFilter


@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(),
});
}
}

这是我的单元测试,2 个断言在这里完成。 第一个断言是检查是否调用了 mockLogger.error。它正在工作。 第二个断言检查是否调用了 response.code(status(.send((。 但是收到此错误。 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();
});
});

我认为使用 Nest 测试模块为异常过滤器编写测试用例很简单。在下面检查我是如何实现相同的:

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

我做了这样的事情,它奏效了!

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()

据我了解,我们总是必须通过一个"期望"的模拟。

在过滤器中,您有...(expection.getResponse() as object),但exception没有getResponse()的功能。相反,您需要先切换到 http 上下文

我稍微修改了一下以使消息正常工作。我收到undefined消息,但我的测试没有通过。所以我不得不修改如下:

import { Test, TestingModule } from '@nestjs/testing';
import { HttpStatus, HttpException, Logger } from '@nestjs/common';
import { GlobalExceptionsFilter } from './http-filter.exception';
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: GlobalExceptionsFilter;
beforeEach(async () => {
jest.clearAllMocks();
const module: TestingModule = await Test.createTestingModule({
providers: [
GlobalExceptionsFilter,
{
provide: Logger,
useValue: mockAppLoggerService,
},
],
}).compile();
service = module.get<GlobalExceptionsFilter>(GlobalExceptionsFilter);
});
describe('All exception filter tests', () => {
it('should be defined', () => {
expect(service).toBeDefined();
});
it('Http exception', () => {
service.catch(
new HttpException({ message: 'Sample 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: 'Sample Exception',
status: false,
});
});
});
});

我的测试最终通过了

PASS  src/utils/exceptions/http-filter.exception.spec.ts
System header validation service
All exception filter tests
✓ should be defined (6 ms)
✓ Http exception (5 ms)
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.225 s, estimated 2 s

最新更新