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

如何在NestJS中对Roles装饰器进行单元测试?

测试NestJS自定义Roles装饰器的方法

测试装饰器的元数据设置逻辑

自定义Roles装饰器本质是通过SetMetadata往目标方法/类上写入元数据,测试时可以用NestJS的Reflector类读取元数据,验证写入是否正确。

示例测试代码:

import { Reflector } from '@nestjs/core';
import { Roles } from './roles.decorator';

describe('Roles Decorator', () => {
  let reflector: Reflector;

  beforeEach(() => {
    reflector = new Reflector();
  });

  it('应正确设置指定的角色元数据', () => {
    class TestController {
      @Roles('admin', 'editor')
      testMethod() {}
    }

    const roles = reflector.get<string[]>('roles', TestController.prototype.testMethod);
    expect(roles).toEqual(['admin', 'editor']);
  });

  it('空参数时应设置空数组元数据', () => {
    class TestController {
      @Roles()
      testMethod() {}
    }

    const roles = reflector.get<string[]>('roles', TestController.prototype.testMethod);
    expect(roles).toEqual([]);
  });
});

结合RolesGuard的集成测试

Roles装饰器通常配合权限守卫使用,下面以常见的RolesGuard为例,测试守卫是否根据装饰器的元数据正确判断权限:

假设你的RolesGuard实现如下:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!roles) return true;
    const request = context.switchToHttp().getRequest();
    return roles.some(role => request.user?.roles?.includes(role));
  }
}

对应的测试代码:

import { Test } from '@nestjs/testing';
import { ExecutionContext } from '@nestjs/common';
import { RolesGuard } from './roles.guard';
import { Reflector } from '@nestjs/core';

describe('RolesGuard 权限验证', () => {
  let guard: RolesGuard;
  let reflector: Reflector;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      providers: [RolesGuard, Reflector],
    }).compile();

    guard = moduleRef.get<RolesGuard>(RolesGuard);
    reflector = moduleRef.get<Reflector>(Reflector);
  });

  it('用户拥有指定角色时应允许请求', () => {
    const context = {
      getHandler: jest.fn(),
      switchToHttp: jest.fn(() => ({
        getRequest: jest.fn(() => ({ user: { roles: ['admin'] } })),
      })),
    } as unknown as ExecutionContext;

    jest.spyOn(reflector, 'get').mockReturnValue(['admin']);
    expect(guard.canActivate(context)).toBe(true);
  });

  it('用户缺少指定角色时应拒绝请求', () => {
    const context = {
      getHandler: jest.fn(),
      switchToHttp: jest.fn(() => ({
        getRequest: jest.fn(() => ({ user: { roles: ['user'] } })),
      })),
    } as unknown as ExecutionContext;

    jest.spyOn(reflector, 'get').mockReturnValue(['admin']);
    expect(guard.canActivate(context)).toBe(false);
  });

  it('未指定角色时应直接允许请求', () => {
    const context = {
      getHandler: jest.fn(),
      switchToHttp: jest.fn(() => ({ getRequest: jest.fn(() => ({}) ) })),
    } as unknown as ExecutionContext;

    jest.spyOn(reflector, 'get').mockReturnValue(undefined);
    expect(guard.canActivate(context)).toBe(true);
  });
});

关键测试要点

  • 单独测试装饰器时,核心是验证SetMetadata是否正确写入了roles键对应的元数据。
  • 集成测试时,重点模拟请求上下文和用户角色信息,验证守卫逻辑是否与装饰器的元数据匹配。
  • 可以用Jest的mock方法快速模拟Reflector的读取行为,减少测试依赖。

内容的提问来源于stack exchange,提问作者Christian Guimarães

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 02:57:13