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

如何测试NestJS认证守卫抛出异常?测试失败求助

NestJS AuthenticationGuard 无Authorization头场景测试方案

核心问题排查方向

测试失败通常出在这三个环节:

  • 守卫逻辑是否明确在无token时抛出UnauthorizedException
  • 测试用例是否正确模拟了无Authorization头的请求上下文
  • Jest是否用正确的断言方式捕获异常

守卫代码逻辑修正(若存在漏洞)

先确保你的守卫在无Authorization头时直接抛出异常,示例代码如下:

import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class AuthenticationGuard implements CanActivate {
  constructor(private authService: AuthService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const authHeader = request.headers.authorization;

    // 无Authorization头直接抛出异常
    if (!authHeader) {
      throw new UnauthorizedException('未提供授权头');
    }

    // 其余token验证逻辑
    const token = authHeader.split(' ')[1];
    const user = await this.authService.validateToken(token);
    if (!user) {
      throw new UnauthorizedException('无效token');
    }
    request.user = user;
    return true;
  }
}

正确的测试用例写法

用Jest模拟请求上下文,重点确保请求头中无authorization字段,然后断言守卫抛出异常:

import { AuthenticationGuard } from './auth.guard';
import { UnauthorizedException } from '@nestjs/common';
import { ExecutionContext } from '@nestjs/common';
import { AuthService } from './auth.service';

describe('AuthenticationGuard', () => {
  let guard: AuthenticationGuard;
  let mockAuthService: jest.Mocked<AuthService>;

  beforeEach(() => {
    // 模拟AuthService依赖
    mockAuthService = { validateToken: jest.fn() } as any;
    guard = new AuthenticationGuard(mockAuthService);
  });

  it('无Authorization头时抛出UnauthorizedException', async () => {
    // 构造无授权头的请求上下文
    const mockContext = {
      switchToHttp: () => ({
        getRequest: () => ({
          headers: {}, // 空headers,确保authorization字段不存在
        }),
      }),
    } as unknown as ExecutionContext;

    // 断言守卫抛出指定异常
    await expect(guard.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);
    await expect(guard.canActivate(mockContext)).rejects.toThrow('未提供授权头');
  });
});

常见错误修正

  • 错误1:模拟上下文时authorization为空字符串
    确保request.headers.authorization是undefined,而非空字符串'',否则部分守卫的startsWith('Bearer ')判断可能会引发非预期逻辑。
  • 错误2:用resolves代替rejects断言
    因为守卫抛出的是异常,必须用rejects来断言,使用resolves会直接导致测试失败。
  • 错误3:上下文模拟不完整
    必须保证switchToHttp().getRequest()返回的对象包含完整的headers结构,否则守卫无法正确获取请求头信息。

进阶:用Test模块模拟完整环境

如果需要更贴近真实运行场景的测试,可使用Nest内置的Test模块创建测试容器:

import { Test } from '@nestjs/testing';
import { AuthenticationGuard } from './auth.guard';
import { AuthService } from './auth.service';
import { ExecutionContext } from '@nestjs/common';

describe('AuthenticationGuard(完整模块测试)', () => {
  let guard: AuthenticationGuard;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      providers: [
        AuthenticationGuard,
        {
          provide: AuthService,
          useValue: { validateToken: jest.fn() },
        },
      ],
    }).compile();

    guard = moduleRef.get<AuthenticationGuard>(AuthenticationGuard);
  });

  it('无Authorization头时抛出异常', async () => {
    const mockContext = {
      switchToHttp: () => ({
        getRequest: () => ({ headers: {} }),
      }),
    } as ExecutionContext;

    await expect(guard.canActivate(mockContext)).rejects.toThrow(UnauthorizedException);
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:44:57