使用templateUrl进行unit testingAngularJS指令
我有一个定义了templateUrl
的AngularJS指令。 我正在用Jasmine进行unit testing。
我的茉莉花JavaScript看起来像下面,根据这个build议:
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 spec错误中运行这个时,出现以下错误:
TypeError: Object #<Object> has no method 'passThrough'
我想要的是为templateUrl被加载 – 我不想使用respond
。 我相信这可能与使用ngMock而不是ngMockE2E相关 。 如果这是罪魁祸首,我如何使用后者而不是前者?
提前致谢!
你是正确的,它与ngMock有关。 每次Angulartesting都会自动加载ngMock模块,并且它会初始化模拟$httpBackend
以处理任何使用$http
服务的情况,这包括模板提取。 模板系统尝试通过$http
加载模板,并成为模拟的“意外请求”。
你需要一种方法将模板预加载到$templateCache
以便Angular在请求时可以使用它们,而不使用$http
。
首选解决scheme:Karma
如果你使用Karma来运行你的testing(你应该是),你可以configuration它来为你加载ng-html2js预处理器的模板。 Ng-html2js读取您指定的HTML文件,并将它们转换成预加载$templateCache
的Angular模块。
第1步:在您的karma.conf.js
启用并configuration预处理器
// 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来支撑您的应用程序,这个configuration将起作用
plugins: [ 'karma-phantomjs-launcher', 'karma-jasmine', 'karma-ng-html2js-preprocessor' ], preprocessors: { 'app/views/*.html': ['ng-html2js'] }, ngHtml2JsPreprocessor: { stripPrefix: 'app/', moduleName: 'my.templates' },
第2步:在testing中使用模块
// my-test.js beforeEach(module("my.templates")); // load new module containing templates
作为一个完整的例子,请看Angulartesting专家Vojta Jina的这个典型例子 。 它包括一个完整的设置:karmaconfiguration,模板和testing。
非噶玛解决scheme
如果因为任何原因(我在传统应用程序中有一个不灵活的构build过程)而不使用Karma,并且只是在浏览器中testing,我发现你可以通过使用一个原始的XHR获取模板来避开ngMock接pipe$httpBackend
为真实,并将其插入$templateCache
。 这个解决scheme的灵活性要低得多,但是现在完成这项工作。
// 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 。 它需要一些工作来设置,但是它可以让你在命令行中同时在多个浏览器中运行所有的testing。 所以你可以把它作为持续集成系统的一部分,或者你可以把它作为你编辑器的快捷键。 比alt-tab-refresh-ad-infinitum好得多。
我最终做的是获取模板caching并将视图放在那里。 我没有控制不使用ngMock,事实certificate:
beforeEach(inject(function(_$rootScope_, _$compile_, $templateCache) { $scope = _$rootScope_; $compile = _$compile_; $templateCache.put('path/to/template.html', '<div>Here goes the template</div>'); }));
这个最初的问题可以通过添加这个来解决:
beforeEach(angular.mock.module('ngMockE2E'));
这是因为它默认情况下会尝试在ngMock模块中查找$ httpBackend ,并且它不是满的。
我达到的解决scheme需要jasmine-jquery.js和代理服务器。
我遵循这些步骤:
- 在karma.conf中:
将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 /'; //自定义path,以便您可以在应用程序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
-
运行业力。
我花了一段时间才弄明白这一点,不得不search很多post,我认为这个文件应该更清楚,因为这是一个非常重要的问题。
我的解决scheme
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'); })); });
如果您使用的是Grunt,则可以使用grunt-angular-templates。 它将您的模板加载到templateCache中,并且与您的规格configuration不同。
我的示例configuration:
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'); };
我以与所选解决scheme稍有不同的方式解决了同样的问题。
-
首先,我安装并configuration了业务的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中创build的模块。 在你的Spec.js文件中:
beforeEach(module('myApp', 'to/templates/myTemplate.html'));
-
然后,我使用$ templateCache.get将其存储到一个variables。 在你的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(); }));
-
最后,我以这种方式testing了它。 在你的Spec.js文件中:
describe('element', function() { it('should contain the template', function() { expect(element.html()).toMatch(template); }); });
要将模板htmldynamic加载到$ templateCache中,您可以使用html2js业务预处理器,如此处所述
这可以归结为在conf.js文件中添加模板“ .html”到你的文件以及预处理器= {' .html':'html2js'};
并使用
beforeEach(module('..')); beforeEach(module('...html', '...html'));
进入你的jstesting文件
如果您使用Karma,请考虑使用karma-ng-html2js-preprocessor预编译外部HTML模板,并避免让Angular在testing执行期间尝试HTTP GET。 我努力了这个为我们的几个 – 在我的情况templateUrl的部分path在正常的应用程序执行期间解决,但不是在testing期间 – 由于应用程序与testing目录结构的差异。
如果您将jasmine-maven-plugin和RequireJS一起使用,则可以使用文本插件将模板内容加载到variables中,然后将其放入模板caching中。
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); })); }); });
如果你在你的testing中使用了requirejs,那么你可以使用'text'插件来拉入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(); })); }); });
我将所有模板编译为templatecache来解决此问题。 我正在使用gulp,你也可以find类似的解决办法。 我的templateUrls在指令中,模态看起来像
`templateUrl: '/templates/directives/sidebar/tree.html'`
-
在我的package.json中添加一个新的npm包
"gulp-angular-templatecache": "1.*"
-
在gulp文件中添加templatecache和一个新的任务:
var templateCache = require('gulp-angular-templatecache'); ... ... gulp.task('compileTemplates', function () { gulp.src([ './app/templates/**/*.html' ]).pipe(templateCache('templates.js', { transformUrl: function (url) { return '/templates/' + url; } })) .pipe(gulp.dest('wwwroot/assets/js')); });
-
在index.html中添加所有js文件
<script src="/assets/js/lib.js"></script> <script src="/assets/js/app.js"></script> <script src="/assets/js/templates.js"></script>
-
请享用!