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

Jest单元测试中@UsesPipes()验证装饰器未触发问题

问题原因
  • NestJS的管道(Pipe)是框架请求生命周期的一部分,依赖Nest的DI容器和装饰器元数据处理机制。当你直接通过new MyController(...)实例化控制器并调用方法时,这只是普通的JavaScript函数调用,Nest不会解析控制器上的@UsePipes装饰器,也不会触发管道的transform方法。
  • 只有当请求通过Nest的路由处理器分发时,框架才会读取方法上的装饰器元数据,自动实例化并执行对应的管道逻辑。
解决办法

方法1:使用NestJS官方测试工具创建测试模块(推荐)

通过@nestjs/testing提供的Test.createTestingModule构建测试模块,让Nest负责控制器的实例化和装饰器逻辑处理,管道会自动触发。

修改后的测试代码示例:

import { Test } from '@nestjs/testing';
import { MyController } from './my.controller';
import { PostPublicationDmValidationPipe } from './my-validation.pipe';

describe('test MyController Create method', () => {
  let controller: MyController;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      controllers: [MyController],
      providers: [PostPublicationDmValidationPipe], // 注册管道
    }).compile();

    controller = moduleRef.get<MyController>(MyController);
  });

  test("should throw error when type is not x", async () => {
    const myPostBody: PostBodyDto = { 'type':'y', /* 其他必填字段 */ };
    const mockedFile = { filename: 'test.txt', mimetype: 'text/plain', buffer: Buffer.from('test') } as unknown as File;
    const mockedRequest = { headers: {}, body: myPostBody, file: mockedFile } as unknown as Request;

    await expect(controller.Create(myPostBody, mockedRequest, mockedFile)).rejects.toThrow();
  });
});

方法2:手动调用管道的transform方法

如果不想依赖测试模块,也可以在测试中手动实例化管道,调用其transform方法验证参数,模拟框架的管道逻辑:

修改后的测试代码示例:

describe('test MyController Create method', () => {
  test("should throw error when type is not x", async () => {
    const myPostBody: PostBodyDto = { 'type':'y', /* 其他必填字段 */ };
    const mockedFile = { filename: 'test.txt', mimetype: 'text/plain', buffer: Buffer.from('test') } as unknown as File;
    const mockedRequest = { headers: {}, body: myPostBody, file: mockedFile } as unknown as Request;
    const myController = new MyController(/* 传入控制器依赖的服务实例 */);
    
    // 手动实例化管道并验证body参数
    const validationPipe = new PostPublicationDmValidationPipe();
    await expect(validationPipe.transform(myPostBody, { type: 'body' })).rejects.toThrow();

    // 若需验证业务逻辑,再调用控制器方法
    // const result = await myController.Create(myPostBody, mockedRequest, mockedFile);
    // ...
  });
});
额外代码修正提示
  • 你的管道代码存在小问题:console.log("hello world! I am invoked!");写在了类的顶层作用域,只会在类加载时执行一次,应放到构造函数中;同时管道需要返回处理后的值,否则参数会变为undefined:
@Injectable()
export class PostPublicationDmValidationPipe implements PipeTransform {
  private readonly logger = new Logger(PostPublicationDmValidationPipe.name);

  constructor() {
    console.log("hello world! I am invoked!");
  }

  transform(value: any, metaData: ArgumentMetadata) {
    if (value.type != 'x') { throw new Error('...') }
    return value; // 必须返回处理后的参数
  }
}
  • 控制器中的@Controller需添加括号:@Controller();@UsesPipes应为Nest官方的@UsePipes(疑似拼写错误)。

内容的提问来源于stack exchange,提问作者inVoKer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 08:45:12