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

AngularJS(v1)项目中基于Jasmine/Karma测试含import语句的Service:如何Mock导入模块

我之前在维护Webpack编译的AngularJS遗留项目时,遇到过几乎一模一样的问题,结合Karma+Jasmine的环境,咱们一步步来解决:

1. 先搞定「SyntaxError: Cannot use import statement outside a module」

这个错误本质是Karma默认不识别ES6的import语法,得让它通过Webpack+Babel来处理所有JS文件。你需要检查karma.conf.js的配置,确保这几个关键部分到位:

  • 给源文件和测试文件加上webpack预处理器
  • 配置Webpack用Babel转译ES6代码
  • 加载Angular和AngularMocks的顺序要正确

示例配置片段:

// karma.conf.js
const path = require('path');

module.exports = function(config) {
  config.set({
    frameworks: ['jasmine'],
    // 先加载Angular核心和Mock库,再加载业务代码和测试文件
    files: [
      'node_modules/angular/angular.js',
      'node_modules/angular-mocks/angular-mocks.js',
      'src/**/*.js',
      'tests/**/*.spec.js'
    ],
    // 让Webpack处理所有需要转译的JS文件
    preprocessors: {
      'src/**/*.js': ['webpack'],
      'tests/**/*.spec.js': ['webpack']
    },
    webpack: {
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env'] // 转译ES6到兼容环境的代码
              }
            }
          }
        ]
      },
      resolve: {
        // 这里后面用来配置模块Mock的别名
      }
    },
    webpackMiddleware: {
      stats: 'errors-only' // 只显示Webpack的错误信息,避免冗余输出
    },
    browsers: ['ChromeHeadless'] // 用无头Chrome跑测试,适合CI环境
  });
};

2. Mock导入的myMember模块

针对Webpack环境,最稳妥的Mock方式是用Webpack别名把真实的my-module替换成我们的Mock文件,这样所有用到该模块的地方都会自动使用Mock版本,不需要在每个测试文件里重复处理。

步骤2.1 创建Mock文件

在测试目录下新建一个Mock文件,结构和真实模块保持一致:

// tests/mocks/my-module.mock.js
// 模拟真实模块的导出结构,用Jasmine Spy来追踪调用
const myMember = {
  // 比如真实模块有个getUser方法,我们Mock它
  getUser: jasmine.createSpy('getUser').and.returnValue({ id: 123, name: 'Mock User' }),
  // 其他需要Mock的方法/属性都在这里定义
};

export default myMember;

步骤2.2 在Webpack配置里添加别名

回到karma.conf.js的webpack.resolve.alias,加上:

resolve: {
  alias: {
    'my-module': path.resolve(__dirname, 'tests/mocks/my-module.mock.js')
  }
}

步骤2.3 编写测试用例

现在测试文件里可以正常import,并且直接使用Mock好的myMember:

// tests/core/myService.spec.js
import myMember from 'my-module';
import 'angular-mocks';

describe('myService', () => {
  let myService;
  let anotherServiceMock;

  beforeEach(() => {
    // Mock Angular依赖的anotherService
    anotherServiceMock = {
      fetchData: jasmine.createSpy('fetchData').and.returnValue([])
    };

    // 加载app.core模块,并替换依赖
    angular.mock.module('app.core', ($provide) => {
      $provide.value('anotherService', anotherServiceMock);
    });

    // 注入我们要测试的服务
    inject((_myService_) => {
      myService = _myService_;
    });
  });

  it('should call myMember.getUser when fetching user info', () => {
    // 调用服务里依赖myMember的方法
    myService.loadUser();
    
    // 验证Mock的方法被正确调用
    expect(myMember.getUser).toHaveBeenCalled();
    expect(myMember.getUser).toHaveBeenCalledWith(jasmine.any(String));
  });
});

3. 解决文件层级导致的执行顺序问题

你提到测试文件放在深层目录时import正常,大概率是Karma的files加载顺序导致的。确保在karma.conf.js的files数组里:

  • 先加载第三方依赖(Angular、AngularMocks)
  • 再加载业务核心模块(比如src/core/*.js)
  • 最后加载测试文件(tests/**/*.spec.js)

如果用Webpack的话,其实Webpack会自动处理模块依赖顺序,只要你的业务代码里的import和Angular模块定义是正确的,一般不会有问题。

另一种灵活的Mock方式(无需Webpack别名)

如果只是某个测试文件需要特殊Mock,可以用babel-plugin-rewire来动态替换模块导出,但需要额外配置Babel。不过对于大多数场景,Webpack别名的方式更简洁通用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 08:52:45