使用 templateUrl 对 AngularJS 指令进行单元测试

使用 AngularJS。

有指示。

指令定义了 templateUrl

指令需要单元测试。

目前使用 Jasmine 进行单元测试。

这个 推荐这样的代码:

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 needs loading as-is

无法使用 respond

可能与使用 ngMock而不是使用 NgMockE2E有关。

70688 次浏览

我最后所做的就是获取模板缓存并将视图放入其中。我无法控制不使用 ngMock,结果是:

beforeEach(inject(function(_$rootScope_, _$compile_, $templateCache) {
$scope = _$rootScope_;
$compile = _$compile_;
$templateCache.put('path/to/template.html', '<div>Here goes the template</div>');
}));

要动态地将模板 html 加载到 $templateCache 中,您可以只使用 html2js karma 预处理器,如 给你所解释的那样

这可以归结为将模板“ < em > . html”添加到 Conf.js 文件中的文件中 我也是 预处理器 = { ” . html’: ‘ html2js’ };

和使用

beforeEach(module('..'));


beforeEach(module('...html', '...html'));

进入 js 测试文件

我找到的解决方案需要 jasmine-jquery. js 和一个代理服务器。

I followed these steps:

  1. In karma.conf:

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

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

添加一个代理服务器,该代理服务器将为您的装置提供服务

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

    describe('MySpec', function() { 变量 $范围,模板; GetFixture () . fixturesPath = ‘ public/partals/’;//custompath,这样您就可以为应用程序中使用的真实模板提供服务 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. Run a server on your app's root directory

    Python-m SimpleHTTPServer 3502

  3. 因果报应。

It took my a while to figure this out, having to search many posts, I think the documentation about this should be clearer, as it is such an important issue.

这个最初的问题可以通过以下方法解决:

beforeEach(angular.mock.module('ngMockE2E'));

这是因为它在缺省情况下尝试在 模仿模块中查找 $httpBackend,但是它并没有满。

如果您正在使用咕噜,您可以使用咕噜角模板。它在 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');


};

你说的没错,和 ngMock 有关。NgMock 模块为每个 Angular 测试自动加载,它初始化模拟 $httpBackend以处理对 $http服务的任何使用,其中包括模板获取。模板系统试图通过 $http加载模板,它成为模拟的“意外请求”。

你需要一种方法来预加载到 $templateCache的模板,使他们已经可用时,角度要求他们,而不使用 $http

The Preferred Solution: Karma

如果您使用 因果报应来运行测试(您应该这样做) ,那么您可以将其配置为使用 Nghtml2js预处理器为您加载模板。Ng-html2js 读取您指定的 HTML 文件,并将它们转换为一个预加载 $templateCache的角度模块。

步骤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"
},

如果你使用 文书脚手架你的应用程序这个配置将工作

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

作为一个完整的例子,看看这个 角度测试大师 Vojta Jina 的典型例子,它包含了一个完整的设置: 业力配置、模板和测试。

A Non-Karma Solution

如果你因为某种原因不使用 Karma (我在遗留应用程序中有一个不灵活的构建过程)而只是在浏览器中测试,我发现你可以通过使用一个原始的 XHR 来获取模板并将其插入到 $templateCache中来绕过 ngMock 对 $httpBackend的接管。这个解决方案的灵活性要差得多,但它可以暂时完成工作。

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

说真的。使用 因果报应。设置它需要一些工作,但它允许您从命令行在多个浏览器中同时运行所有测试。因此,您可以将其作为持续集成系统的一部分,并且/或者您可以将其作为编辑器中的快捷键。比 Alt-Tab-Refresh-ad-∞好多了。

我用与选择的解决方案略有不同的方法解决了同一个问题。

  1. 首先,我安装并配置了 Nghtml2js插件 karma. In the karma.conf.js file :

    preprocessors: {
    'path/to/templates/**/*.html': 'ng-html2js'
    },
    ngHtml2JsPreprocessor: {
    // you might need to strip the main directory prefix in the URL request
    stripPrefix: 'path/'
    }
    
  2. Then I loaded the module created in the beforeEach. In your Spec.js file :

    beforeEach(module('myApp', 'to/templates/myTemplate.html'));
    
  3. Then I used $templateCache.get to store it into a variable. In your Spec.js file :

    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. Finally, I tested it this way. In your Spec.js file:

    describe('element', function() {
    it('should contain the template', function() {
    expect(element.html()).toMatch(template);
    });
    });
    

如果您正在使用 Karma,请考虑使用 Karma-ng-html2js-预处理器来预编译您的外部 HTML 模板,并避免在测试执行期间让 Angular 尝试 HTTP GET 它们。在我的案例中,templateUrl 的部分路径在正常的应用程序执行过程中得到了解决,但是在测试过程中没有得到解决,由于应用程序和测试目录结构的不同,我曾经为此纠结过几次。

如果您正在使用 Jasmine-Maven-plugin和 RequreJS,您可以使用 文本插件将模板内容加载到一个变量中,然后将其放入模板缓存中。


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


});
});

我的解决办法是:

返回文章页面

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

返回文章页面

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

If you use requirejs in your tests then you can use the 'text' plugin to pull in the html template and put it in the $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 来解决这个问题。 我用的是咕噜,你也可以找到类似的解决方案。 我的 templateUrls 在指令中,模态看起来像

`templateUrl: '/templates/directives/sidebar/tree.html'`
  1. 在 package.json 中添加一个新的 npm 包

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

  2. 在 Gulp file 中添加 templatecache 和一个新任务:

    Var templateCache = demand (‘ Gulp-angle-templecache’) ; ... ... Task (‘ developeTemplate’,function (){ Gulp.src ([ ’./app/template/* */* . html’ ]) . tube (templateCache (‘ templates.js’,{ Function (url){ 返回’/template/’+ url; } })) . tube (Gulp.dests (‘ wwwroot/asset/js’)) ; });

  3. Add all js files in index.html

    < script src = “/asset/js/lib.js”> < script src = “/asset/js/app.js”> < script src = “/asset/js/templates.js”>

  4. 好好享受吧!