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

NestJS命名空间内用@Injectable导致Jest测试报错求助

解决NestJS命名空间内@Injectable装饰器导致Jest测试报错问题

问题描述

在NestJS项目中,将类通过TypeScript命名空间导出,并在命名空间内部的类上添加@Injectable()装饰器后,运行Jest测试时出现错误:

× Expected '{', got 'namespace'

移除@Injectable()装饰器后测试可正常运行,但业务需求要求保留该装饰器。

原因分析

TypeScript命名空间(namespace)与装饰器结合的语法,在ts-jest(Jest的TypeScript编译器)的解析逻辑中存在兼容性冲突。装饰器语法糖作用于命名空间内部类时,编译器的语法解析顺序或处理逻辑出现异常,导致报错。

解决方案

方案1:改用ES模块导出替代命名空间(推荐)

NestJS原生推荐使用ES模块规范,命名空间属于TypeScript旧语法,改用模块导出更适配Nest生态,同时避免语法兼容性问题。

修改业务代码:

// search-case.ts
import { Injectable } from '@nestjs/common';
import { InterfaceRepository } from './your-repo-path';
import { Case } from './your-case-path';
import { RequestSearchCase, ResponseSearchCase } from './your-dto-path';

@Injectable()
export class SearchCase {
  constructor(private casesRepository: InterfaceRepository<Case.Case>) { }

  async execute(request: RequestSearchCase): Promise<ResponseSearchCase> {
    const searchResult = await this.casesRepository.search(request.search);
    return { searchResult };
  }
}

// 若需分组导出,可创建模块文件统一导出
// search-case-group.ts
export * from './search-case';
// 或模拟命名空间结构(可选)
export const SearchCase = {
  SearchCase: SearchCase
};

测试代码调整引用:

import { SearchCase } from './search-case';
// 若用分组导出则改为:
// import { SearchCase } from './search-case-group';

describe('Search Case', () => {
  it('should return a case matching the substring', async () => {
    const casesRepository = new InMemoryCaseRepository();
    const searchCase = new SearchCase(casesRepository);

    const createCase = new Case.Case({
      utente: 'utente test',
      caseOrigin: 'case origin test',
      reportingDate: new Date(),
      reporterName: 'Emanuela Xavier',
      disease: 'disease test',
    });

    await casesRepository.create(createCase);
    
    const response = await searchCase.execute({ search: 'Ema' });

    expect(response.searchResult.length).toBe(1);
    expect(response.searchResult[0].reporterName).toContain('Ema');
  });
});

方案2:调整ts-jest配置兼容命名空间语法

若必须保留命名空间,可修改编译配置让ts-jest正确解析命名空间与装饰器的组合语法:

  1. 更新tsconfig.json的编译选项:
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": false // 关闭单文件编译,确保支持命名空间跨文件解析
  }
}
  1. 修改jest.config.js的ts-jest配置:
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  transform: {
    '^.+\\.tsx?$': ['ts-jest', {
      tsconfig: 'tsconfig.json',
      diagnostics: false // 关闭诊断日志,避免干扰
    }]
  }
};

方案3:手动调用装饰器函数(兜底方案)

绕过装饰器语法糖,直接调用Injectable装饰器函数作用于类,避免编译器解析异常:

import { Injectable } from '@nestjs/common';

export namespace SearchCase {
  export class SearchCase {
    constructor(private casesRepository: InterfaceRepository<Case.Case>) { }

    async execute(request: RequestSearchCase): Promise<ResponseSearchCase> {
      const searchResult = await this.casesRepository.search(request.search);
      return { searchResult };
    }
  }
  // 手动应用Injectable装饰器
  Injectable()(SearchCase);
}

总结

优先选择方案1,ES模块更符合NestJS的设计理念,能减少后续维护中的兼容性问题;若业务强制要求保留命名空间,依次尝试方案2和方案3。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 06:20:27