使用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和代理服务器。

我遵循这些步骤:

  1. 在karma.conf中:

将jasmine-jquery.js添加到您的文件中

 files = [ JASMINE, JASMINE_ADAPTER, ..., jasmine-jquery-1.3.1, ... ] 

添加一个代理服务器,将服务器您的灯具

 proxies = { '/' : 'http://localhost:3502/' }; 
  1. 在你的规格

    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(); }) }); 

    });

  2. 在应用程序的根目录中运行服务器

    python -m SimpleHTTPServer 3502

  3. 运行业力。

我花了一段时间才弄明白这一点,不得不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稍有不同的方式解决了同样的问题。

  1. 首先,我安装并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/' } 
  2. 然后我加载了在beforeEach中创build的模块。 在你的Spec.js文件中:

     beforeEach(module('myApp', 'to/templates/myTemplate.html')); 
  3. 然后,我使用$ 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(); })); 
  4. 最后,我以这种方式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'` 
  1. 在我的package.json中添加一个新的npm包

    "gulp-angular-templatecache": "1.*"

  2. 在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')); });

  3. 在index.html中添加所有js文件

    <script src="/assets/js/lib.js"></script> <script src="/assets/js/app.js"></script> <script src="/assets/js/templates.js"></script>

  4. 请享用!