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

为何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.

原因分析

测试跳过鉴权逻辑的核心原因有两点:

  1. 直接调用Resolver方法绕过了请求管道:NestJS的守卫(Guard)、拦截器等中间件仅在框架处理HTTP/GraphQL请求的管道中生效。当前测试是直接实例化Resolver类并调用其createCategory方法,相当于绕过了整个请求处理流程,守卫根本没有被触发执行的机会。
  2. 测试模块未加载鉴权相关依赖:测试模块中仅注册了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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 01:05:02