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
相关产品推荐
相关产品推荐

