Тестирование перехватчиков может быть одной из самых сложных частей тестирования приложения Nest JS из-за ExecutionContext
и возврата правильного значения из next
.
Давайте начнем с ExecutionContext
:
У вас все в порядке с вашим текущим контекстом, важно то, что у вас есть метод switchToHttp()
, если вы используете HTTP (как и вы) и что все, что возвращается 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
объект должен выглядеть примерно так: htis:
const next = {
handle: () => of()
}
Но это довольно просто c и мало помогает с регистрацией ответов или работой с отображением ответов , Чтобы сделать функцию обработчика более устойчивой, мы всегда можем сделать что-то вроде
const next = {
handle: jest.fn(() => of(myDataObject)),
}
Теперь, если необходимо, вы можете переопределить функцию через Jest, но в целом этого достаточно. Теперь ваш nest.handle()
вернет Observable и будет доступен через Rx JS операторов.
Теперь для тестирования Observable вы почти правы с подпиской, с которой работаете, и это здорово ! Один из тестов может выглядеть следующим образом:
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
наблюдателя (обратный вызов подписки), поскольку нет точек данных, возвращаемых по своей сути (хотя это все равно будет работать в любом случае из-за того, как работают наблюдаемые объекты).
Если Вы хотели бы увидеть пример из реальной жизни (хотя и с библиотекой создания макетов), вы можете проверить мой код для пакета журналов, над которым я работаю.