【问题标题】:Jest Unit Test for Nodejs/NestJS ExceptionFilter Catch MethodNodejs/NestJS ExceptionFilter Catch 方法的 Jest 单元测试
【发布时间】:2020-03-30 23:22:46
【问题描述】:

这是我用 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 断言在这里完成。 第一个 assert 是检查是否调用了 mockLogger.error。这是工作。 第二个断言检查是否调用了 response.code(status).send()。 但得到这个错误。 期望(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();
  });

});

【问题讨论】:

  • 您找到解决方案了吗?我对此有点卡住了。
  • @tsadkanyitbarek 检查下面我的答案,我已经成功编写测试用例了。

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


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

      我认为使用 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'
                  });
              });
          });
      });
      
      

      【讨论】:

      • 当我尝试运行此测试时,我得到一个 ctx.getRequest() is not a function。知道为什么吗? @iAviator
      • 我已经编辑了上面的代码模拟 getRequest fn 添加。您可以将 jest.fn() 替换为您的自定义模拟
      猜你喜欢
      • 1970-01-01
      • 2020-05-15
      • 2022-01-06
      • 2019-09-14
      • 1970-01-01
      • 1970-01-01
      • 2019-08-23
      • 2018-03-11
      • 2018-09-30
      相关资源
      最近更新 更多