javascript - 测试服务时未调用 NestJS UseFilter
问题描述
我有一个 NestJS 控制器: search.controller.ts
import { Body, Controller, Post, Req, UseFilters } from '@nestjs/common';
import { HttpExceptionFilter } from '../exception/http-exception.filter';
import { SearchData } from './models/search-data.model';
import { SearchResults } from 'interfaces';
import { SearchService } from './search.service';
@Controller('')
@UseFilters(HttpExceptionFilter)
export class SearchController {
constructor(private searchService: SearchService) {}
@Post('api/search')
async searchDataById(
@Body() searchData: SearchData,
@Req() req
): Promise<SearchResults> {
return await this.searchService.getSearchResultsById(
searchData,
token
);
}
}
此搜索控制器使用名为HttpExceptionFilter的过滤器。每当抛出HttpException时,都会触发此过滤器。我创建了扩展HttpException的ServiceException。每当出现错误时,我都会抛出 new ServiceException() 。
HttpExceptionFilter
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException
} from '@nestjs/common';
import { ErrorDetails } from './error-details.interface';
import { HTTP_ERRORS } from './errors.constant';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();
const api = exception.getResponse() as string;
const errorDetails = this.getErrorDetails(api, status);
response.status(status).json({
status: status,
title: errorDetails.title,
message: errorDetails.message
});
}
private getErrorDetails(api: string, status: string | number): ErrorDetails {
const errorDetails: ErrorDetails = {
title: HTTP_ERRORS.GENERAL.ERROR.title,
message: HTTP_ERRORS.GENERAL.ERROR.message
};
// if rejection status is logged out or toke expired then redirect to login
if (
HTTP_ERRORS.hasOwnProperty(api) &&
HTTP_ERRORS[api].hasOwnProperty(status)
) {
errorDetails.title = HTTP_ERRORS[api][status].title;
errorDetails.message = HTTP_ERRORS[api][status].message;
}
return errorDetails;
}
}
服务异常
import { HttpException } from '@nestjs/common';
export class ServiceException extends HttpException {
constructor(private details, private code) {
super(details, code);
}
}
搜索服务.ts
import { APIS } from '../app.constants';
import { HttpService, HttpStatus, Injectable } from '@nestjs/common';
import { SearchData, SearchResultSchema } from './models/search-data.model';
import { AppConfigService } from '../app-config/app-config.service';
import { AxiosResponse } from 'axios';
import { DataMappingPayload } from './models/data-mapping-payload.model';
import { SEARCH_SCHEMAS } from './search.constants';
import { SearchModelMapper } from './search-model-mapper.service';
import { SearchResults } from '@delfi-data-management/interfaces';
import { ServiceException } from '../exception/service.exception';
@Injectable()
export class SearchService {
constructor(
private searchModelMapper: SearchModelMapper,
private configService: AppConfigService,
private readonly httpService: HttpService
) {}
// eslint-disable-next-line max-lines-per-function
async getSearchResultsById(
searchData: SearchData,
stoken: string
): Promise<SearchResults> {
if (searchData.filters.collectionId && searchData.viewType) {
if (
Object.values(SEARCH_SCHEMAS).indexOf(
searchData.viewType as SEARCH_SCHEMAS
) !== -1
) {
try {
...... some code cant paste here
return this.searchModelMapper.generateSearchResults(
kinds,
mappingPayload,
searchResultsAPI.data.results
);
} catch (error) {
throw new ServiceException(
APIS.SEARCH,
HttpStatus.INTERNAL_SERVER_ERROR
);
}
} else {
throw new ServiceException(APIS.SEARCH, HttpStatus.BAD_REQUEST);
}
} else if (!searchData.filters.collectionId) {
throw new ServiceException(APIS.SEARCH, HttpStatus.BAD_REQUEST);
} else {
throw new ServiceException(APIS.SEARCH, HttpStatus.BAD_REQUEST);
}
}
现在事情永远不会到达单元测试中的HttpExceptionFilter文件
search.service.spec.ts
beforeEach(async () => {
const app = await Test.createTestingModule({
imports: [AppConfigModule, HttpModule, SearchModule]
}).compile();
searchService = app.get<SearchService>(SearchService);
});
it('should throw error message if viewType not provided', () => {
const searchDataquery = {
filters: {
collectionId: 'accd'
},
viewType: ''
};
const result = searchService.getSearchResultsById(searchDataquery, 'abc');
result.catch((error) => {
expect(error.response).toEqual(
generateSearchResultsErrorResponse.viewTypeError
);
});
});
抛出new ServiceException,内部触发HttpException不触发HttpExceptionFilter是有原因的吗?
解决方案
过滤器在单元测试期间没有绑定,因为它们需要 Nest 正确绑定的请求上下文(这是 Nest 处理请求生命周期的方式)。由于单元测试没有传入的 HTTP 请求,因此生命周期仅被视为您明确调用的内容,在这种情况下:SearchSerivce
. 如果您正在寻找测试过滤器,您应该设置和 e2e 类型测试,您使用 supertest 发送 HTTP 请求并允许您的过滤器在请求期间捕获。
推荐阅读
- highcharts - 在 dotnet highcharts 中点击 DRILLEDOWN PIE Charts 后打开柱形图
- eclipse - 如何覆盖应用和恢复默认值
- java - 在串口上发送和接收响应
- php - 通过joomla中的媒体管理器上传时如何允许unicode?
- testng - TestNG 6.12+ 中未执行的测试方法
- javascript - 使用订阅的返回值来创建可订阅的承诺
- pgadmin - PGAdmin 中的查询参数
- c++ - Boost Asio SSL 第二次以后无法接收数据(第一次正常)
- html - 元素被迫向右浮动,但不应该
- wordpress - WordPress Jupiter 主题 GDPR 配置