如何测试使用createParamDecorator实现的自定义RequestHeader装饰器
测试方案
核心逻辑
createParamDecorator传入的工厂函数就是你需要测试的核心代码,无需耦合Nest的请求流程单独测试即可,同时调整模拟的ExecutionContext结构覆盖全逻辑分支。
1. 调整原代码结构(不影响原有功能,仅方便测试)
把装饰器的工厂函数单独抽离导出,避免你需要额外处理装饰器封装逻辑:
import { plainToClass, ClassType } from 'class-transformer'; import { validateOrReject } from 'class-validator'; import { createParamDecorator, ExecutionContext } from '@nestjs/common'; // 单独导出工厂函数用于测试 export const requestHeaderFactory = async (value: ClassType<any>, ctx: ExecutionContext) => { const headers = ctx.switchToHttp().getRequest().headers; const dto = plainToClass(value, headers, { excludeExtraneousValues: true }); await validateOrReject(dto); return dto; }; export const RequestHeader = createParamDecorator(requestHeaderFactory);
注:原代码
ClassType导入路径错误,新版本class-transformer已将该类型移至根导出,直接修改导入路径即可解决报错。
2. 编写测试用例(以Jest为例)
首先构造测试用的DTO和模拟ExecutionContext实例:
import { IsString, IsNumber } from 'class-validator'; import { ExecutionContext } from '@nestjs/common'; import { requestHeaderFactory } from './your-decorator-file-path'; // 测试用DTO class TestHeaderDto { @IsString() 'x-user-id': string; @IsNumber() 'x-app-version': number; } // 通用模拟ctx模板 const createMockCtx = (headers: Record<string, any>) => { return { switchToHttp: jest.fn().mockReturnThis(), getRequest: jest.fn().mockReturnValue({ headers }) } as unknown as ExecutionContext; };
覆盖三个核心测试场景即可达到100%覆盖率:
describe('RequestHeader 装饰器', () => { it('传入合法header时返回正确DTO实例', async () => { const mockCtx = createMockCtx({ 'x-user-id': '1001', 'x-app-version': 2 }); const result = await requestHeaderFactory(TestHeaderDto, mockCtx); expect(result).toBeInstanceOf(TestHeaderDto); expect(result['x-user-id']).toBe('1001'); expect(result['x-app-version']).toBe(2); }); it('header缺失必填字段时抛出验证错误', async () => { // 缺少必填字段x-user-id const mockCtx = createMockCtx({ 'x-app-version': 2 }); await expect(requestHeaderFactory(TestHeaderDto, mockCtx)).rejects.toThrow(); }); it('header字段类型不匹配时抛出验证错误', async () => { // x-app-version类型错误 const mockCtx = createMockCtx({ 'x-user-id': '1001', 'x-app-version': 'beta' }); await expect(requestHeaderFactory(TestHeaderDto, mockCtx)).rejects.toThrow(); }); });
3. 原测试方案问题说明
你之前让所有调用返回null的mock方式跳过了所有核心逻辑分支,所以测试虽然通过但覆盖率为0。按上述方式构造真实的header参数和完整调用链mock后,工厂函数内的每一行代码都会被执行到。
内容的提问来源于stack exchange,提问作者deadlyfrog
相关产品推荐
相关产品推荐

