如何测试NestJS认证守卫抛出异常?测试失败求助
核心问题排查方向
测试失败通常出在这三个环节:
- 守卫逻辑是否明确在无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
相关产品推荐
相关产品推荐

