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

在Apollo Server 4+Jest+TS中Mock ESM函数未生效问题排查

问题:Jest Mock AuthService方法未生效,Resolver仍调用原方法

我正在用Jest和TypeScript搭建Apollo Server 4的集成测试,Resolver代码中调用AuthService类的checkIfAuthorized方法做权限校验,想要Mock该方法使其固定返回指定值,但测试未生效——Resolver执行时还是会调用原方法。

相关代码

测试文件 src/tests/integration/integration.spec.ts

import {ApolloServer} from "@apollo/server";

import {readFileSync} from 'fs';
const typeDefs = readFileSync('./schema.graphql', {encoding: 'utf-8'});
import {resolvers} from "../../resolvers/resolvers.js";
import {jest} from "@jest/globals";
import {AuthService} from "../../utils/authorization";

jest.mock('../../utils/authorization');

describe('Your test suite description', () => {
   
    let authorization: AuthService;

    beforeEach(async () => {
        console.log("In before each ")
        authorization = new AuthService( // 缺少闭合括号,语法错误
    });

    it('returns hello with the provided name', async () => {
        const checkIfAuthorizedMock = jest.spyOn(authorization, 'checkIfAuthorized');
        checkIfAuthorizedMock.mockResolvedValue(await Promise.resolve());

        const testServer = new ApolloServer({
            typeDefs,
            resolvers,
        });
        
        const response = await testServer.executeOperation({
            query: `
      mutation addUser($userId: String!) {
        addUser(
          ....
      }
    `,
            variables: {
                userId: "1234",
            },
            operationName: 'AddUser'
        },);

        console.log("Response " + JSON.stringify(response))

    });
});

src/utils/authorization.ts

export class AuthService {
    async checkIfAuthorized(context?: MyContext, userId?: string) {
     // 原权限校验逻辑
    }
}

src/resolvers/resolver.ts

export const addUserResolver = async (_parent: any, args: { input: AddUser },
                                                     _context: MyContext
): Promise<User> => {
    try {
        await authorization.checkIfAuthorized(_context, args.input.userId)
        return await some_func()
    } catch (err) {
        // 错误处理
    }
}

tsconfig.json

{
  "compilerOptions": {
    "rootDirs": ["src"],
    "outDir": "dist",
    "lib": ["es2020"],
    "target": "es2020",
    "strict": true,
    "module": "esnext",
    "noErrorTruncation": true,
    "preserveConstEnums": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "types": ["node", "jest"],
  },
  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node"
  }
}

jest-config

import type { JestConfigWithTsJest } from 'ts-jest'

const jestConfig: JestConfigWithTsJest = {
    extensionsToTreatAsEsm: ['.ts'],
    moduleNameMapper: {
        '^(\\.{1,2}/.*)\\.js$': '$1',
    },
    transform: {
        '^.+\\.tsx?$': [
            'ts-jest',
            {
                useESM: true,
            },
        ],
    },
}

export default jestConfig

错误原因及解决方法

1. 导入顺序错误

jest.mock必须在导入依赖模块(比如resolvers)之前执行,否则resolvers模块加载时会使用未被Mock的原始AuthService类。

修正测试文件导入顺序:

import { jest } from "@jest/globals";
// 先执行mock,再导入其他依赖
jest.mock('../../utils/authorization');

import { ApolloServer } from "@apollo/server";
import { readFileSync } from 'fs';
const typeDefs = readFileSync('./schema.graphql', { encoding: 'utf-8' });
import { resolvers } from "../../resolvers/resolvers.js";
import { AuthService } from "../../utils/authorization";

2. Mock对象不匹配

你在测试中Mock的是自己创建的authorization实例,但Resolver中使用的是另一个独立实例(大概率是在resolvers模块初始化时创建的),所以Mock不会生效。需要直接Mock AuthService类的原型方法,覆盖所有实例的调用。

修正测试用例中的Mock逻辑:

it('returns hello with the provided name', async () => {
    // Mock类的原型方法,所有实例都会使用这个Mock
    const mockCheck = jest.spyOn(AuthService.prototype, 'checkIfAuthorized');
    // 因为原方法是async,返回void的话直接传undefined即可
    mockCheck.mockResolvedValue(undefined);

    const testServer = new ApolloServer({
        typeDefs,
        resolvers,
    });
    
    const response = await testServer.executeOperation({
        query: `
      mutation addUser($userId: String!) {
        addUser(userId: $userId) {
          id
          name
        }
      }
    `,
        variables: {
            userId: "1234",
        },
        operationName: 'AddUser'
    });

    console.log("Response ", JSON.stringify(response));
    // 验证Mock方法确实被调用
    expect(mockCheck).toHaveBeenCalledWith(expect.any(Object), "1234");
});

3. 语法错误

beforeEach中new AuthService(缺少闭合括号,修正为:

beforeEach(async () => {
    console.log("In before each ")
    authorization = new AuthService();
});

额外注意事项

如果Resolver中的authorization是模块级别的单例(比如在resolvers文件顶部直接const authorization = new AuthService()),必须确保jest.mock在导入resolvers之前执行,这样单例创建时使用的是Mock后的类,才能让Mock生效。

内容的提问来源于stack exchange,提问作者salt-pepper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 20:13:08