单元测试AngularJS指令与templateUrl

Wor*_*red 121 jasmine angularjs angularjs-directive

我有一个templateUrl定义的AngularJS指令.我正在尝试用Jasmine进行单元测试.

根据以下建议,我的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');
            });

        });
    });
});
Run Code Online (Sandbox Code Playgroud)

当我在我的Jasmine规范错误中运行它时,我收到以下错误:

TypeError: Object #<Object> has no method 'passThrough'
Run Code Online (Sandbox Code Playgroud)

我想要的是模板加载原型 - 我不想使用respond.我相信这可能与使用ngMock而不是ngMockE2E有关.如果这是罪魁祸首,我该如何使用后者而不是前者呢?

提前致谢!

Sle*_*rph 186

你说它与ngMock有关是正确的.每次Angular测试都会自动加载ngMock模块,并初始化模拟$httpBackend以处理$http服务的任何使用,包括模板提取.模板系统尝试加载模板$http,它成为模拟的"意外请求".

您需要一种方法将模板预先加载到$templateCacheAngular请求它们时,它们已经可用,而不使用$http.

首选解决方案:业力

如果您正在使用Karma运行测试(并且您应该),则可以将其配置为使用ng-html2js预处理器为您加载模板.Ng-html2js读取您指定的HTML文件并将它们转换为预先加载的Angular模块$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"
},
Run Code Online (Sandbox Code Playgroud)

如果您使用Yeoman来构建您的应用程序,此配置将起作用

plugins: [ 
  'karma-phantomjs-launcher', 
  'karma-jasmine', 
  'karma-ng-html2js-preprocessor' 
], 

preprocessors: { 
  'app/views/*.html': ['ng-html2js'] 
}, 

ngHtml2JsPreprocessor: { 
  stripPrefix: 'app/', 
  moduleName: 'my.templates' 
},
Run Code Online (Sandbox Code Playgroud)

第2步:在测试中使用该模块

// my-test.js

beforeEach(module("my.templates"));    // load new module containing templates
Run Code Online (Sandbox Code Playgroud)

有关完整示例,请查看Angular测试大师Vojta Jina的典型示例.它包括整个设置:karma配置,模板和测试.

非Karma解决方案

如果你因为任何原因没有使用Karma(我在遗留应用程序中有一个不灵活的构建过程)并且只是在浏览器中进行测试,我发现你可以$httpBackend通过使用原始XHR获取真实模板来解决ngMock的攻击并将其插入$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);
}));
Run Code Online (Sandbox Code Playgroud)

但是说真的.使用Karma.设置需要一些工作,但它允许您从命令行一次在多个浏览器中运行所有测试.因此,您可以将其作为持续集成系统的一部分,和/或您可以将其作为编辑器的快捷键.比alt-tab-refresh-ad-infinitum好多了.

  • 这可能是显而易见的,但是如果其他人遇到同样的事情并在这里寻找答案:如果没有添加`preprocessors`文件模式(例如``path/to/templates/**/)我就无法工作*.html"`)到`karma.conf.js`中的`files`部分. (6认同)
  • 另一件事:你需要安装karma-ng-html2js-preprocessor(`npm install --save-dev karma-ng-html2js-preprocessor`),然后将它添加到你的`karma.conf.js`的插件部分,根据http://stackoverflow.com/a/19077966/859631. (5认同)

Wor*_*red 37

我最终做的是获取模板缓存并将视图放在那里.事实证明,我无法控制不使用ngMock:

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

  • 以下是我对这种方法的抱怨......现在,如果我们要将一大段html作为字符串注入到模板缓存中,那么当我们更改前端的html时,我们会做些什么呢? ?也改变测试中的html?IMO是一个不可持续的答案,也是我们使用templateUrl选项模板的原因.即使我非常不喜欢将html作为指令中的大字符串 - 这是最不可能更新两个html地方的解决方案.哪个不需要很多成像,html可以随着时间的推移不匹配. (26认同)

bul*_*are 12

这个初始问题可以通过添加:

beforeEach(angular.mock.module('ngMockE2E'));
Run Code Online (Sandbox Code Playgroud)

那是因为它试图在默认情况下在ngMock模块中找到$ httpBackend并且它不是满的.


Tom*_*ero 8

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

我按照以下步骤操作:

  1. 在karma.conf中:

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

files = [
    JASMINE,
    JASMINE_ADAPTER,
    ...,
    jasmine-jquery-1.3.1,
    ...
]
Run Code Online (Sandbox Code Playgroud)

添加一个代理服务器,为您的灯具提供服务

proxies = {
    '/' : 'http://localhost:3502/'
};
Run Code Online (Sandbox Code Playgroud)
  1. 在你的规格

    describe('MySpec',function(){var $ scope,template; jasmine.getFixtures().fixturesPath ='public/partials /'; //自定义路径,这样你就可以提供你在app之前使用的真实模板(函数) (){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();
        })
    });
    
    Run Code Online (Sandbox Code Playgroud)

    });

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

    python -m SimpleHTTPServer 3502

  3. 跑业力.

我花了一段时间来弄明白,不得不搜索很多帖子,我认为关于这个的文档应该更清楚,因为它是一个非常重要的问题.


bar*_*tek 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);
  });
}
Run Code Online (Sandbox Code Playgroud)

karma.config.js:

files: [
  //(...)
  'test/karma-utils.js',
  'test/mock/**/*.js',
  'test/spec/**/*.js'
],
Run Code Online (Sandbox Code Playgroud)

考试:

'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');
  }));
});
Run Code Online (Sandbox Code Playgroud)


Tom*_*ero 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');

};
Run Code Online (Sandbox Code Playgroud)


gle*_*tre 6

我以与所选解决方案略有不同的方式解决了同样的问题.

  1. 首先,我为业力安装并配置了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/'
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 然后我加载了在beforeEach中创建的模块.在Spec.js文件中:

    beforeEach(module('myApp', 'to/templates/myTemplate.html'));
    
    Run Code Online (Sandbox Code Playgroud)
  3. 然后我使用$ 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();
    }));
    
    Run Code Online (Sandbox Code Playgroud)
  4. 最后,我用这种方式测试了它.在Spec.js文件中:

    describe('element', function() {
      it('should contain the template', function() {
        expect(element.html()).toMatch(template);
      });
    });
    
    Run Code Online (Sandbox Code Playgroud)