如何为NestJS中的参数装饰器编写单元测试?
自定义NestJS装饰器的单元测试方案
一、直接测试装饰器本身
你的AuthUser装饰器基于createParamDecorator实现,核心逻辑是从请求上下文的args[0]中提取principal字段。测试时只需模拟对应的上下文参数,验证返回值是否符合预期:
import { AuthUser } from './users.decorator'; import { ExecutionContext } from '@nestjs/common'; describe('AuthUser Decorator', () => { it('应正确返回请求中的principal对象', () => { // 模拟测试用的用户信息 const mockPrincipal = { id: 1, username: 'test-user' }; // 模拟ExecutionContext,构造符合装饰器读取逻辑的结构 const mockContext = { args: [{ principal: mockPrincipal }] } as unknown as ExecutionContext; // 调用装饰器,传入参数'user'和模拟上下文 const result = AuthUser('user', mockContext); expect(result).toEqual(mockPrincipal); }); });
二、结合控制器测试(验证真实场景)
在控制器中使用装饰器时,需要结合守卫、服务一起测试,确保装饰器能正确将用户对象注入到方法参数中:
测试步骤:
- 模拟
LogbooksService和UsersGuard,跳过真实的认证逻辑 - 在守卫的
canActivate方法中,给请求对象添加模拟的principal - 调用控制器的
createLogbook方法,验证服务是否接收到正确的用户参数
import { Test } from '@nestjs/testing'; import { LogbooksController } from './logbooks.controller'; import { LogbooksService } from './logbooks.service'; import { UsersGuard } from '../users/users.guard'; import { CreateLogbookDto } from './dto/CreateLogbook.dto'; import { ExecutionContext } from '@nestjs/common'; describe('LogbooksController', () => { let controller: LogbooksController; let logbooksService: jest.Mocked<LogbooksService>; beforeEach(async () => { const moduleRef = await Test.createTestingModule({ controllers: [LogbooksController], providers: [ { provide: LogbooksService, useValue: { createLogbook: jest.fn().mockResolvedValue({ id: 1, ...new CreateLogbookDto() }) } } ] }) // 替换UsersGuard为模拟实现,添加测试用的principal .overrideGuard(UsersGuard) .useValue({ canActivate: jest.fn((context: ExecutionContext) => { const req = context.switchToHttp().getRequest(); req.principal = { id: 1, username: 'test-user' }; return true; }) }) .compile(); controller = moduleRef.get<LogbooksController>(LogbooksController); logbooksService = moduleRef.get(LogbooksService); }); it('创建日志时应正确传递认证用户', async () => { const createDto: CreateLogbookDto = { title: '测试日志', content: '测试内容' }; const mockUser = { id: 1, username: 'test-user' }; await controller.createLogbook(createDto, mockUser); // 验证服务方法被调用时传入了正确的用户对象 expect(logbooksService.createLogbook).toHaveBeenCalledWith(createDto, mockUser); }); });
可选优化:让装饰器适配不同HTTP适配器
当前装饰器直接读取req.args[0],仅适配Express场景。如果需要兼容Fastify,建议修改为NestJS标准的上下文读取方式:
// 优化后的AuthUser装饰器 import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export const AuthUser = createParamDecorator((data: string, context: ExecutionContext) => { const request = context.switchToHttp().getRequest(); return request.principal; });
这种写法更通用,后续测试也可以通过context.switchToHttp().getRequest()统一设置请求参数,无需关注底层框架的args结构。
内容的提问来源于stack exchange,提问作者Xeyos
相关产品推荐
相关产品推荐

