Ruby on Rails集成AngularJS 1单元测试遇阻:teaspoon-jasmine与angular-mocks问题
我刚帮几个朋友踩过AngularJS 1 + Teaspoon-Jasmine测试的坑,给你梳理几个最常见的排查方向,应该能帮你解决问题:
Teaspoon对文件加载顺序的要求特别严格——AngularJS核心库必须在angular-mocks之前加载,你的测试文件又要在这两个之后。
检查你的teaspoon_env.rb或者Teaspoon配置里的脚本加载顺序,比如正确的配置应该是这样:
# 示例配置,确保顺序不能乱 config.scripts = [ 'vendor/assets/javascripts/angular.js', 'vendor/assets/javascripts/angular-mocks.js', 'app/assets/javascripts/**/*.js', 'spec/javascripts/**/*.spec.js' ]
如果顺序反了,angular.mock根本就不存在,自然会抛出“angular is undefined”这类基础错误。
很多报错都是因为测试里引用的模块名和实际控制器所属的模块名不匹配。比如你的控制器是这么定义的:
angular.module('MyRailsApp', []).controller('HomeCtrl', function($scope) { $scope.greeting = 'Hi there!'; });
那测试里必须先加载MyRailsApp这个模块,绝对不能写错名字:
describe('HomeCtrl', function() { // 这里的模块名必须和实际定义的完全一致 beforeEach(angular.mock.module('MyRailsApp')); var $controller; beforeEach(angular.mock.inject(function(_$controller_) { $controller = _$controller_; })); it('should set greeting correctly', function() { var $scope = {}; var ctrl = $controller('HomeCtrl', { $scope: $scope }); expect($scope.greeting).toBe('Hi there!'); }); });
如果模块名写错,inject会直接抛出“Failed to instantiate module”的错误。
有时候Teaspoon的沙箱环境会限制全局变量访问,你可以在测试文件开头加两行调试代码,看看angular和angular.mock是否存在:
console.log(angular); // 应该输出Angular的核心对象 console.log(angular.mock); // 应该包含module和inject方法
如果输出undefined,说明angular-mocks没加载成功——要么是文件路径写错了,要么是Rails的asset pipeline没正确编译这个文件。可以去public/assets目录下看看编译后的文件里有没有angular-mocks的代码,或者直接在Teaspoon测试页面的源码里搜索angular-mocks.js,确认它已经被引入。
有些开发者喜欢用module()和inject()代替angular.mock.module()和angular.mock.inject(),但如果你的项目里还有其他库(比如jQuery)也定义了module变量,就会直接冲突。这种情况下,老老实实用完整的angular.mock.module和angular.mock.inject调用,能避开很多莫名其妙的问题。
AngularJS 1.x对Jasmine的版本有要求,比如AngularJS 1.7.x最好搭配Jasmine 2.x,用Jasmine 3.x可能会出现兼容性问题。你可以检查Gemfile里的teaspoon-jasmine版本,比如指定一个稳定的匹配版本:
gem 'teaspoon-jasmine', '~> 2.4.0' # 这个版本和AngularJS 1.7系列适配度很高
如果还是解决不了,把具体的报错信息贴出来(比如是“Unknown provider”还是“angular is not defined”),能更精准地定位问题。
内容的提问来源于stack exchange,提问作者Jerrod

