首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NestJ请求和响应拦截器单元测试

NestJ请求和响应拦截器单元测试
EN

Stack Overflow用户
提问于 2020-01-07 07:43:25
回答 1查看 5.7K关注 0票数 3

我想记录API的传入请求和传出响应。我创建了一个请求拦截器和一个响应拦截器,如下所述

https://docs.nestjs.com/interceptors

因此,请求拦截器只记录请求对象。

代码语言:javascript
复制
@Injectable()
export class RequestInterceptor implements NestInterceptor {
  private readonly logger: Logger = new Logger(RequestInterceptor.name, true);

  public intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const { originalUrl, method, params, query, body } = context.switchToHttp().getRequest();

    this.logger.debug({ originalUrl, method, params, query, body }, this.intercept.name);

    return next.handle();
  }
}

响应拦截器等待传出响应,并在稍后记录状态代码和响应对象。

代码语言:javascript
复制
@Injectable()
export class ResponseInterceptor implements NestInterceptor {
  private readonly logger: Logger = new Logger(ResponseInterceptor.name, true);

  public intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const { statusCode } = context.switchToHttp().getResponse();

    return next.handle().pipe(
      tap((responseData: any) =>
        this.logger.debug({ statusCode, responseData }, this.intercept.name),
      ),
    );
  }
}

我想测试它们,但不幸的是,我几乎没有测试经验。我试着从请求拦截器开始,并想出了这个

代码语言:javascript
复制
const executionContext: any = {
  switchToHttp: jest.fn().mockReturnThis(),
  getRequest: jest.fn().mockReturnThis(),
};

const nextCallHander: CallHandler<any> = {
  handle: jest.fn(),
};

describe('RequestInterceptor', () => {
  let interceptor: RequestInterceptor;

  beforeEach(() => {
    interceptor = new RequestInterceptor();
  });

  describe('intercept', () => {
    it('should fetch the request object', (done: any) => {
      const requestInterception: Observable<any> = interceptor.intercept(executionContext, nextCallHander);

      requestInterception.subscribe({
        next: value => {
          // ... ??? ...
        },
        error: error => {
          throw error;
        },
        complete: () => {
          done();
        },
      });
    });
  });
});

目前,我不知道如何传递给下一个回调,但是当我试图按原样运行测试时,它说requestInterception变量是未定义的。因此,在到达下一个回调之前,测试将失败。所以我得到的错误信息是

TypeError:无法读取未定义的属性“订阅”

我还试着测试响应拦截器,并想出了以下方法

代码语言:javascript
复制
const executionContext: any = {
  switchToHttp: jest.fn().mockReturnThis(),
  getResponse: jest.fn().mockReturnThis()
};

const nextCallHander: CallHandler<any> = {
  handle: jest.fn()
};

describe("ResponseInterceptor", () => {
  let interceptor: ResponseInterceptor;

  beforeEach(() => {
    interceptor = new ResponseInterceptor();
  });

  describe("intercept", () => {
    it("should fetch the statuscode and response data", (done: any) => {
      const responseInterception: Observable<any> = interceptor.intercept(
        executionContext,
        nextCallHander
      );

      responseInterception.subscribe({
        next: value => {
          // ...
        },
        error: error => {
          throw error;
        },
        complete: () => {
          done();
        }
      });
    });
  });
});

这一次我在拦截器上发现了一个错误

TypeError:无法读取未定义的属性“管道”

有人能帮我正确地测试这两个拦截器吗?

提前感谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-01-07 19:11:38

测试拦截器可能是测试NestJS应用程序的最具挑战性的部分之一,因为ExecutionContext和从next返回正确的值。

让我们从ExecutionContext开始

您已经在当前上下文中设置了一个好方法,重要的是,如果您使用的是HTTP (就像您一样),那么您有一个switchToHttp()方法,而switchToHttp()返回的任何内容都有一个getResponse()getRequest()方法(或者两者都使用)。在那里,getRequest()getResponse()方法应该返回从req和res中使用的值,例如res.statusCodereq.originalUrl。我喜欢在同一个拦截器上有传入和传出,所以我的context对象通常如下所示:

代码语言:javascript
复制
const context = {
  switchToHttp: jest.fn(() => ({
    getRequest: () => ({
      originalUrl: '/',
      method: 'GET',
      params: undefined,
      query: undefined,
      body: undefined,
    }),
    getResponse: () => ({
      statusCode: 200,
    }),
  })),
  // method I needed recently so I figured I'd add it in
  getType: jest.fn(() => 'http')
}

这只会保持上下文的轻巧和易于使用。当然,您可以根据日志记录的需要,使用更复杂的值来替换这些值。

现在,对于有趣的部分,CallHandler对象。CallHandler有一个handle()函数,它返回一个可观察的。至少,这意味着您的next对象需要如下所示:

代码语言:javascript
复制
const next = {
  handle: () => of()
}

但这是非常基本的,在记录响应或处理响应映射方面没有多大帮助。为了使处理程序函数更健壮,我们总是可以这样做

代码语言:javascript
复制
const next = {
  handle: jest.fn(() => of(myDataObject)),
}

现在,如果需要的话,您可以通过Jest覆盖函数,但一般来说,这就足够了。现在,您的next.handle()将返回一个可观察的,并将通过RxJS运算符可浏览。

现在,为了测试可观察到的功能,您几乎可以使用您正在使用的订阅,这很好!其中一个测试可以如下所示:

代码语言:javascript
复制
describe('ResponseInterceptor', () => {
  let interceptor: ResponseInterceptor;
  let loggerSpy = jest.spyOn(Logger.prototype, 'debug');

  beforeEach(() => {
    interceptor = new ResponseInterceptor();
  });

  afterEach(() => {
    loggerSpy.resetMock();
  });

  describe('intercept', () => {
    it('should fetch the request object', (done: any) => {
      const responseInterceptor: Observable<any> = interceptor.intercept(executionContext, nextCallHander);

      responseInterceptor.subscribe({
        next: value => {
          // expect the logger to have two parameters, the data, and the intercept function name
          expect(loggerSpy).toBeCalledWith({statusCode: 200, responseData: value}, 'intercept');
        },
        error: error => {
          throw error;
        },
        complete: () => {
          // only logging one request
          expect(loggerSpy).toBeCalledTimes(1);
          done();
        },
      });
    });
  });
});

其中executionContextcallHandler来自我们前面设置的值。

一个类似的想法可以在RequestInterceptor中完成,但是只记录在观察者的complete部分(订阅回调),因为没有固有的数据点返回(但由于可观察的工作方式,它仍然可以工作)。

如果您希望看到一个真实的示例(尽管有一个模拟创建库),您可以为我正在处理的日志程序包提供看看我的代码

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59624156

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档