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

时间:2019-12-06 00:21:56

标签: node.js typescript unit-testing jestjs nestjs

这是我用Typescript为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();
  });

});

3 个答案:

答案 0 :(得分:3)

我认为使用Nest Testing模块为异常过滤器编写测试用例很简单。请在下面查看我如何达到同样的效果:

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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

据我了解,我们总是必须为“期望”传递一个模拟。