Jest.js NestJS中间件中Axios HttpService管道的单元测试

rjee0c15  于 2023-09-28  发布在  Jest
关注(0)|答案(1)|浏览(168)

中间件从微服务端点获取JSON文档并将其附加到请求。
好的路径测试是可以的,但是我不能让坏的路径测试抛出ForbiddenException并阻止它调用next()。
在Jest测试之外,如果中间件无法获取json文档,则会阻止请求。
任何帮助是赞赏:)
中间件:

import { ForbiddenException, Injectable, NestMiddleware } from '@nestjs/common'
import { HttpService } from '@nestjs/axios'
import { NextFunction, Request, Response } from 'express'
import { catchError, firstValueFrom } from 'rxjs'
import { LoggerService } from '../logger.service'

@Injectable()
export class MyMiddleware implements NestMiddleware {
  constructor(
    private logger: LoggerService,
    private httpService: HttpService,
  ) {}

  async use(req: Request, res: Response, next: NextFunction) {

    const response = await firstValueFrom(
      this.httpService
        .get('https://myservice/document-endpoint', {
          headers: {
            Cookie: req.headers['cookie'],
          },
        })
        .pipe(
          catchError((error: Error) => {
            this.logger.error(error.message);
            throw new ForbiddenException()
          }),
        ),
    )

    req.document = response.data.value
    next()
  }
}

测试:
“坏路径”测试是问题所在... nextFunction方法被调用,我无法设法让它抛出错误。

import { MyMiddleware } from './mymiddleware'
import { NextFunction, Request, Response } from 'express'
import { LoggerService } from '../logger/logger.service'
import { of } from 'rxjs'
import { createMock } from '@golevelup/nestjs-testing'
import { HttpService } from '@nestjs/axios'
import { AxiosResponse } from 'axios'

describe('Authorization middleware', () => {
  let middleware: MyMiddleware
  let mockRequest: Partial<Request>
  let mockResponse: Partial<Response>
  let nextFunction: NextFunction = jest.fn()
  let mockHttpService = createMock<HttpService>()
  
  mockRequest = {
    headers: {
      cookie: 'idToken=asdasdasd'
    }

  beforeEach(async () => {
    mockResponse = {
      json: jest.fn(),
    }

    middleware = new MyMiddleware(new LoggerService(), mockHttpService)
  })

  test('bad path', async () => {
    jest.clearAllMocks()

    const mockDocumentResponse: AxiosResponse = {
      status: 404, 
      statusText: '',
      headers: {},
      config: {},
      data: { error: 'Not Found' }
    }

    const httpSpy = jest.spyOn(mockHttpService, 'get')
      .mockReturnValue(of(mockDocumentResponse))

    await middleware.use(mockRequest as Request, mockResponse as Response, nextFunction)
    expect(nextFunction).toBeCalledTimes(0)
  })

  test('good path', async () => {
    jest.clearAllMocks()

    const mockDocumentResponse: AxiosResponse = {
      status: 200, 
      statusText: '',
      headers: {},
      config: {},
      data: {
        value: 'example document',
      }
    }

    jest.spyOn(mockHttpService, 'get').mockImplementationOnce(() => of(mockDocumentResponse));
    await middleware.use(mockRequest as Request, mockResponse as Response, nextFunction)
    expect(nextFunction).toBeCalledTimes(1)
  })

})
xe55xuns

xe55xuns1#

我从来没有抽出时间来发布解决方案,但似乎这个问题得到了一些访问,所以在这里。
关键点是NestJS集成了RxJS作为一等公民,并接受了Observables的概念。HttpService(axios的 Package 器)默认返回Observables。
Observable与promise(https://rxjs.dev/guide/observable)不同,在中间件代码中,这是通过使用firstValueFrom方法(从RxJS导入)来处理的,该方法将Observable转换为常规的JavaScript promise。
因此,当测试“坏路径”时,我们正在使用Observables模拟错误。这可以通过返回一个新的Observable来完成,该Observable使用s.error(err)方法立即抛出错误。

import { Observable, of } from 'rxjs'
...

test('bad path', async () => {
  jest.clearAllMocks()

  const err = { response: 'resp', status: '500' }

  // Instead of returning an error response, we simulate throwing an error.
  const httpSpy = jest.spyOn(mockHttpService, 'get')
    .mockImplementationOnce(() => new Observable(subscriber => subscriber.error(err)))

  // Handle the expected ForbiddenException
  await middleware.use(mockRequest as Request, mockResponse as Response, nextFunction)
    .catch((error) => {
      expect(error).toBeInstanceOf(ForbiddenException)
      expect(nextFunction).toBeCalledTimes(0)
    })
})

相关问题