为何Jest测试跳过用户鉴权仍能通过NestJS GraphQL resolver用例?
问题:NestJS集成测试中跳过GraphQL Resolver鉴权仍通过测试的原因
在NestJS中使用Jest开展集成测试时,出现跳过用户鉴权却仍能通过GraphQL resolver所有测试用例的情况。相关代码与测试详情如下:
相关代码
category.resolver.ts(Resolver实现)
import { Args, Mutation, Resolver, Query, Context } from '@nestjs/graphql'; import { CategoryService } from './category.service'; import { UseGuards } from '@nestjs/common'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { RolesGuard } from '../auth/guards/roles.guard'; import { UserRole } from '@prisma/client'; import { PrivilegesList } from '../privileges/user-privileges'; import { PermissionsGuardOR } from '../auth/guards/permissions-or.guard'; import { Roles } from '../auth/decorators/roles.decorator'; import { Permissions } from '../auth/decorators/permissions.decorator'; import { Category, PaginatedCategory } from './entities/category.entity'; @Resolver() export class CategoryResolver { constructor(public readonly categoryService: CategoryService) {} @Mutation(() => Category) @UseGuards(JwtAuthGuard, RolesGuard, PermissionsGuardOR) @Roles(UserRole.SUPERADMIN) @Permissions([PrivilegesList.CATEGORY_MANAGEMENT.CAPABILITIES.CREATE]) createCategory(@Args('name') name: string): Promise<Category> { return this.categoryService.createCategory(name); } }
category.resolver.spec.ts(测试代码)
import { Test, TestingModule } from '@nestjs/testing'; import { CategoryResolver } from './category.resolver'; import { CategoryService } from './category.service'; import { PrismaService } from '../prisma/prisma.service'; describe('CategoryResolver', () => { let categoryResolver: CategoryResolver; let appSetup; let createdCategory; let updatedCategory; let userRole; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [CategoryResolver, CategoryService, PrismaService], }) .compile(); categoryResolver = module.get<CategoryResolver>(CategoryResolver); }); it('Create category', async () => { let createCategoryInput = 'Java'; createdCategory = await categoryResolver.createCategory( createCategoryInput, ); expect(200); expect(createdCategory).toHaveProperty(['name']); }); }
测试输出结果
/app # yarn test category.resolver.spec.ts yarn run v1.22.22 $ jest category.resolver.spec.ts PASS src/category/category.resolver.spec.ts (7.958 s) CategoryResolver ✓ Create category (198 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 1.062 s Ran all test suites matching /category.resolver.spec.ts/i. Done in 2.49s.
原因分析
测试跳过鉴权逻辑的核心原因有两点:
- 直接调用Resolver方法绕过了请求管道:NestJS的守卫(Guard)、拦截器等中间件仅在框架处理HTTP/GraphQL请求的管道中生效。当前测试是直接实例化Resolver类并调用其
createCategory方法,相当于绕过了整个请求处理流程,守卫根本没有被触发执行的机会。 - 测试模块未加载鉴权相关依赖:测试模块中仅注册了Resolver、Service和PrismaService,没有引入Auth模块或注册任何守卫提供者,框架无法加载和初始化鉴权逻辑。
另外,测试中的expect(200);是无效断言——它只是检查数值200是否为真,永远会通过,对测试结果没有实际验证作用。
解决方案
根据测试目标不同,有两种可行方案:
方案1:单元测试(专注业务逻辑,模拟鉴权)
如果仅需测试Resolver的业务逻辑,无需验证鉴权,可以在测试模块中替换守卫为模拟实现,让它们始终通过:
beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ CategoryResolver, CategoryService, PrismaService, // 模拟守卫,跳过鉴权 { provide: JwtAuthGuard, useValue: { canActivate: () => true } }, { provide: RolesGuard, useValue: { canActivate: () => true } }, { provide: PermissionsGuardOR, useValue: { canActivate: () => true } }, ], }).compile(); categoryResolver = module.get<CategoryResolver>(CategoryResolver); });
方案2:集成测试(验证完整流程,包括鉴权)
如果要测试包含鉴权在内的完整请求流程,必须通过NestJS测试客户端发送GraphQL请求,触发完整的请求处理管道:
import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { CategoryResolver } from './category.resolver'; import { CategoryService } from './category.service'; import { PrismaService } from '../prisma/prisma.service'; import { AuthModule } from '../auth/auth.module'; describe('CategoryResolver (Integration)', () => { let app: INestApplication; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: true, }), AuthModule, // 加载鉴权模块,初始化守卫和装饰器 ], providers: [CategoryResolver, CategoryService, PrismaService], }).compile(); app = module.createNestApplication(); await app.init(); }); it('should reject unauthenticated create category request', async () => { // 发送未授权的GraphQL请求 const result = await app.executeOperation({ query: `mutation { createCategory(name: "Java") { name } }`, }); // 验证鉴权错误 expect(result.errors).toBeDefined(); expect(result.errors[0].message).toContain('Unauthorized'); }); afterAll(async () => { await app.close(); }); });
这种方式会完整触发请求处理流程,包括守卫的鉴权逻辑,能有效验证鉴权是否正常工作。
内容的提问来源于stack exchange,提问作者Anup Kumar
相关产品推荐
相关产品推荐

