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

使用ConfigurableModuleBuilder的Nest.js测试报Proxy创建错误

解决Nest.js ConfigurableModuleBuilder动态模块测试中的TypeError问题

错误原因

当使用@golevelup/ts-jest的automocker(useMocker(createMock))时,它会尝试为所有依赖自动创建mock。但你的WorkloadIdentityTokenService依赖MODULE_OPTIONS_TOKEN——这是由ConfigurableModuleBuilder生成的注入令牌(通常是字符串或Symbol),不属于对象/类类型。createMock函数需要基于对象创建Proxy,因此无法处理这类令牌,最终抛出TypeError: Cannot create proxy with a non-object as target or handler。

解决方案

方法1:手动提供模块选项

直接在测试模块中为MODULE_OPTIONS_TOKEN提供具体值,避免automocker尝试mock它:

describe('WorkloadIdentityTokenService', () => {
  let service: WorkloadIdentityTokenService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        WorkloadIdentityTokenService,
        {
          provide: MODULE_OPTIONS_TOKEN,
          useValue: { minutesToRefreshTokenBeforeExpiry: 30 }, // 测试用自定义选项
        },
      ],
    })
      .useMocker(createMock)
      .compile();

    service = module.get(WorkloadIdentityTokenService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
});

方法2:配置automocker忽略特定令牌

通过自定义mocker函数,让automocker跳过对MODULE_OPTIONS_TOKEN的处理,手动返回所需选项:

describe('WorkloadIdentityTokenService', () => {
  let service: WorkloadIdentityTokenService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [WorkloadIdentityTokenService],
    })
      .useMocker((token) => {
        if (token === MODULE_OPTIONS_TOKEN) {
          return { minutesToRefreshTokenBeforeExpiry: 30 };
        }
        return createMock(token);
      })
      .compile();

    service = module.get(WorkloadIdentityTokenService);
  });

  // 测试默认值逻辑
  it('should fall back to default value when option is missing', async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        WorkloadIdentityTokenService,
        { provide: MODULE_OPTIONS_TOKEN, useValue: {} },
      ],
    }).compile();

    const service = module.get(WorkloadIdentityTokenService);
    expect(service['minutesToRefreshTokenBeforeExpiry']).toBe(60);
  });
});

方法3:导入完整配置的动态模块

如果需要测试整个动态模块的集成效果,直接使用ConfigurableModuleClass的forRoot方法导入配置好的模块:

describe('WorkloadIdentityTokenService', () => {
  let service: WorkloadIdentityTokenService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports: [
        ConfigurableModuleClass.forRoot({
          minutesToRefreshTokenBeforeExpiry: 45,
        }),
      ],
    }).compile();

    service = module.get(WorkloadIdentityTokenService);
  });

  it('should apply provided module options', () => {
    expect(service['minutesToRefreshTokenBeforeExpiry']).toBe(45);
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:35:14