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
相关产品推荐
相关产品推荐

