我有一个定义了templateUrl
的AngularJS指令。我正在尝试用Jasmine进行单元测试。
根据this的推荐,我的Jasmine JavaScript如下所示:
describe('module: my.module', function () {
beforeEach(module('my.module'));
describe('my-directive directive', function () {
var scope, $compile;
beforeEach(inject(function (_$rootScope_, _$compile_, $injector) {
scope = _$rootScope_;
$compile = _$compile_;
$httpBackend = $injector.get('$httpBackend');
$httpBackend.whenGET('path/to/template.html').passThrough();
}));
describe('test', function () {
var element;
beforeEach(function () {
element = $compile(
'<my-directive></my-directive>')(scope);
angular.element(document.body).append(element);
});
afterEach(function () {
element.remove();
});
it('test', function () {
expect(element.html()).toBe('asdf');
});
});
});
});
当我在我的Jasmine规范错误中运行时,我收到以下错误:
TypeError: Object #<Object> has no method 'passThrough'
我想要的只是按原样加载templateUrl - 我不想使用respond
。我认为这可能与使用ngMock而不是ngMockE2E有关。如果这是罪魁祸首,我该如何使用后者而不是前者?
提前致谢!
答案 0 :(得分:184)
你说它与ngMock有关是正确的。为每个Angular测试自动加载ngMock模块,并初始化模拟$httpBackend
以处理$http
服务的任何使用,其中包括模板提取。模板系统尝试通过$http
加载模板,它成为模拟的“意外请求”。
您需要一种方法将模板预加载到$templateCache
中,以便在Angular请求它们时它们已经可用,而不使用$http
。
如果您使用Karma来运行测试(并且应该是),则可以将其配置为使用ng-html2js预处理器为您加载模板。 Ng-html2js读取您指定的HTML文件,并将它们转换为预先加载$templateCache
的Angular模块。
步骤1:在karma.conf.js
// karma.conf.js
preprocessors: {
"path/to/templates/**/*.html": ["ng-html2js"]
},
ngHtml2JsPreprocessor: {
// If your build process changes the path to your templates,
// use stripPrefix and prependPrefix to adjust it.
stripPrefix: "source/path/to/templates/.*/",
prependPrefix: "web/path/to/templates/",
// the name of the Angular module to create
moduleName: "my.templates"
},
如果您使用Yeoman来构建应用程序,则此配置将起作用
plugins: [
'karma-phantomjs-launcher',
'karma-jasmine',
'karma-ng-html2js-preprocessor'
],
preprocessors: {
'app/views/*.html': ['ng-html2js']
},
ngHtml2JsPreprocessor: {
stripPrefix: 'app/',
moduleName: 'my.templates'
},
第2步:在测试中使用模块
// my-test.js
beforeEach(module("my.templates")); // load new module containing templates
有关完整示例,请查看此canonical example from Angular test guru Vojta Jina。它包括整个设置:karma配置,模板和测试。
如果你因为某种原因没有使用Karma(我在遗留应用程序中有一个不灵活的构建过程)并且只是在浏览器中进行测试,我发现你可以通过使用一个ngmock来接管$httpBackend
原始XHR以获取真实模板并将其插入$templateCache
。这种解决方案的灵活性要低得多,但它现在已经完成了工作。
// my-test.js
// Make template available to unit tests without Karma
//
// Disclaimer: Not using Karma may result in bad karma.
beforeEach(inject(function($templateCache) {
var directiveTemplate = null;
var req = new XMLHttpRequest();
req.onload = function() {
directiveTemplate = this.responseText;
};
// Note that the relative path may be different from your unit test HTML file.
// Using `false` as the third parameter to open() makes the operation synchronous.
// Gentle reminder that boolean parameters are not the best API choice.
req.open("get", "../../partials/directiveTemplate.html", false);
req.send();
$templateCache.put("partials/directiveTemplate.html", directiveTemplate);
}));
但是,说真的。使用Karma。设置需要一些工作,但它允许您从命令行一次在多个浏览器中运行所有测试。因此,您可以将其作为持续集成系统的一部分,和/或您可以将其作为编辑器的快捷键。比alt-tab-refresh-ad-infinitum好多了。
答案 1 :(得分:37)
我最终做的是获取模板缓存并将视图放在那里。事实证明,我无法控制不使用ngMock:
beforeEach(inject(function(_$rootScope_, _$compile_, $templateCache) {
$scope = _$rootScope_;
$compile = _$compile_;
$templateCache.put('path/to/template.html', '<div>Here goes the template</div>');
}));
答案 2 :(得分:12)
这个初始问题可以通过添加:
来解决beforeEach(angular.mock.module('ngMockE2E'));
这是因为它默认情况下会尝试在 ngMock 模块中找到 $ httpBackend 并且它未满。
答案 3 :(得分:8)
我达到的解决方案需要jasmine-jquery.js和代理服务器。
我按照以下步骤操作:
将jasmine-jquery.js添加到您的文件
files = [
JASMINE,
JASMINE_ADAPTER,
...,
jasmine-jquery-1.3.1,
...
]
添加将为您的灯具提供服务的代理服务器
proxies = {
'/' : 'http://localhost:3502/'
};
在您的规范中
describe('MySpec',function(){ var $ scope,template; jasmine.getFixtures()。fixturesPath ='public / partials /'; //自定义路径,以便您可以提供在应用上使用的真实模板 beforeEach(function(){ template = angular.element('');
module('project');
inject(function($injector, $controller, $rootScope, $compile, $templateCache) {
$templateCache.put('partials/resources-list.html', jasmine.getFixtures().getFixtureHtml_('resources-list.html')); //loadFixture function doesn't return a string
$scope = $rootScope.$new();
$compile(template)($scope);
$scope.$apply();
})
});
});
在应用的根目录
上运行服务器python -m SimpleHTTPServer 3502
运行业力。
我花了一段时间来弄明白这一点,不得不搜索很多帖子,我认为关于这个的文档应该更清楚,因为它是一个非常重要的问题。
答案 4 :(得分:7)
我的解决方案:
test/karma-utils.js
:
function httpGetSync(filePath) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/base/app/" + filePath, false);
xhr.send();
return xhr.responseText;
}
function preloadTemplate(path) {
return inject(function ($templateCache) {
var response = httpGetSync(path);
$templateCache.put(path, response);
});
}
karma.config.js
:
files: [
//(...)
'test/karma-utils.js',
'test/mock/**/*.js',
'test/spec/**/*.js'
],
测试:
'use strict';
describe('Directive: gowiliEvent', function () {
// load the directive's module
beforeEach(module('frontendSrcApp'));
var element,
scope;
beforeEach(preloadTemplate('views/directives/event.html'));
beforeEach(inject(function ($rootScope) {
scope = $rootScope.$new();
}));
it('should exist', inject(function ($compile) {
element = angular.element('<event></-event>');
element = $compile(element)(scope);
scope.$digest();
expect(element.html()).toContain('div');
}));
});
答案 5 :(得分:6)
如果您使用的是Grunt,则可以使用grunt-angular-templates。它将模板加载到templateCache中,并且它与您的specs配置完全相同。
我的示例配置:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
ngtemplates: {
myapp: {
options: {
base: 'public/partials',
prepend: 'partials/',
module: 'project'
},
src: 'public/partials/*.html',
dest: 'spec/javascripts/angular/helpers/templates.js'
}
},
watch: {
templates: {
files: ['public/partials/*.html'],
tasks: ['ngtemplates']
}
}
});
grunt.loadNpmTasks('grunt-angular-templates');
grunt.loadNpmTasks('grunt-contrib-watch');
};
答案 6 :(得分:6)
我以与所选解决方案略有不同的方式解决了同样的问题。
首先,我安装并配置了ng-html2js插件 业。在karma.conf.js文件中:
preprocessors: {
'path/to/templates/**/*.html': 'ng-html2js'
},
ngHtml2JsPreprocessor: {
// you might need to strip the main directory prefix in the URL request
stripPrefix: 'path/'
}
然后我加载了在beforeEach中创建的模块。 在您的Spec.js文件中:
beforeEach(module('myApp', 'to/templates/myTemplate.html'));
然后我使用$ templateCache.get将其存储到变量中。 在您的Spec.js文件中:
var element,
$scope,
template;
beforeEach(inject(function($rootScope, $compile, $templateCache) {
$scope = $rootScope.$new();
element = $compile('<div my-directive></div>')($scope);
template = $templateCache.get('to/templates/myTemplate.html');
$scope.$digest();
}));
最后,我用这种方式测试了它。 在您的Spec.js文件中:
describe('element', function() {
it('should contain the template', function() {
expect(element.html()).toMatch(template);
});
});
答案 7 :(得分:4)
要将模板html动态加载到$ templateCache中,您可以使用html2js karma预处理器,如here所述
这归结为将模板“ .html”添加到conf.js文件中的文件中 同样 预处理器= { ' .html':'html2js' };
并使用
beforeEach(module('..'));
beforeEach(module('...html', '...html'));
进入你的js测试文件
答案 8 :(得分:2)
如果您使用的是Karma,请考虑使用karma-ng-html2js-preprocessor预编译外部HTML模板,并避免让Angular在测试执行期间尝试HTTP GET。我为我们的几个人努力奋斗 - 在我的情况下,templateUrl的部分路径在正常应用程序执行期间解决,但在测试期间没有解决 - 由于应用程序与测试目录结构的差异。
答案 9 :(得分:2)
如果您将jasmine-maven-plugin与RequireJS一起使用,则可以使用text plugin将模板内容加载到变量中,然后将其放入模板缓存中。
define(['angular', 'text!path/to/template.html', 'angular-route', 'angular-mocks'], function(ng, directiveTemplate) {
"use strict";
describe('Directive TestSuite', function () {
beforeEach(inject(function( $templateCache) {
$templateCache.put("path/to/template.html", directiveTemplate);
}));
});
});
答案 10 :(得分:2)
如果您在测试中使用requirejs,那么您可以使用&#39;文本&#39;插件将拉入html模板并将其放入$ templateCache。
require(["text!template.html", "module-file"], function (templateHtml){
describe("Thing", function () {
var element, scope;
beforeEach(module('module'));
beforeEach(inject(function($templateCache, $rootScope, $compile){
// VOILA!
$templateCache.put('/path/to/the/template.html', templateHtml);
element = angular.element('<my-thing></my-thing>');
scope = $rootScope;
$compile(element)(scope);
scope.$digest();
}));
});
});