You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 14:24:03