我想记录API的传入请求和传出响应。我创建了一个请求拦截器和一个响应拦截器,如下所述
https://docs.nestjs.com/interceptors
因此,请求拦截器只记录请求对象。
@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();
}
}响应拦截器等待传出响应,并在稍后记录状态代码和响应对象。
@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),
),
);
}
}我想测试它们,但不幸的是,我几乎没有测试经验。我试着从请求拦截器开始,并想出了这个
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:无法读取未定义的属性“订阅”
我还试着测试响应拦截器,并想出了以下方法
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:无法读取未定义的属性“管道”
有人能帮我正确地测试这两个拦截器吗?
提前感谢
发布于 2020-01-07 19:11:38
测试拦截器可能是测试NestJS应用程序的最具挑战性的部分之一,因为ExecutionContext和从next返回正确的值。
让我们从ExecutionContext开始
您已经在当前上下文中设置了一个好方法,重要的是,如果您使用的是HTTP (就像您一样),那么您有一个switchToHttp()方法,而switchToHttp()返回的任何内容都有一个getResponse()或getRequest()方法(或者两者都使用)。在那里,getRequest()或getResponse()方法应该返回从req和res中使用的值,例如res.statusCode或req.originalUrl。我喜欢在同一个拦截器上有传入和传出,所以我的context对象通常如下所示:
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对象需要如下所示:
const next = {
handle: () => of()
}但这是非常基本的,在记录响应或处理响应映射方面没有多大帮助。为了使处理程序函数更健壮,我们总是可以这样做
const next = {
handle: jest.fn(() => of(myDataObject)),
}现在,如果需要的话,您可以通过Jest覆盖函数,但一般来说,这就足够了。现在,您的next.handle()将返回一个可观察的,并将通过RxJS运算符可浏览。
现在,为了测试可观察到的功能,您几乎可以使用您正在使用的订阅,这很好!其中一个测试可以如下所示:
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();
},
});
});
});
});其中executionContext和callHandler来自我们前面设置的值。
一个类似的想法可以在RequestInterceptor中完成,但是只记录在观察者的complete部分(订阅回调),因为没有固有的数据点返回(但由于可观察的工作方式,它仍然可以工作)。
如果您希望看到一个真实的示例(尽管有一个模拟创建库),您可以为我正在处理的日志程序包提供看看我的代码。
https://stackoverflow.com/questions/59624156
复制相似问题