Единичное тестирование Директива AngularJS с шаблономUrl

У меня есть директива AngularJS, которая имеет templateUrl . Я пытаюсь выполнить его тестирование с помощью 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( '')(scope); angular.element(document.body).append(element); }); afterEach(function () { element.remove(); }); it('test', function () { expect(element.html()).toBe('asdf'); }); }); }); }); 

Когда я запускаю это в своей ошибке спецификации Jasmine, я получаю следующую ошибку:

 TypeError: Object # has no method 'passThrough' 

Все, что я хочу, это загрузить templateUrl как есть – я не хочу использовать respond . Я считаю, что это может быть связано с ним, используя ngMock вместо ngMockE2E . Если это преступник, как использовать последний вместо первого?

Заранее спасибо!

Вы правы, что это связано с ngMock. Модуль ngMock автоматически загружается для каждого теста Angular, и он инициализирует mock $httpBackend для обработки любого использования службы $http , которая включает выбор шаблона. Система шаблонов пытается загрузить шаблон через $http и он становится «неожиданным запросом» для макета.

Что вам нужно, чтобы предварительно загрузить шаблоны в шаблон $templateCache чтобы они уже были доступны, когда Angular запрашивает их, без использования $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" }, 

Если вы используете 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 

Для полного примера, посмотрите на этот канонический пример от Angular test guuru Vojta Jina . Он включает в себя всю настройку: конфиг karma, шаблоны и тесты.

Решение без кармы

Если вы не используете Karma по какой-либо причине (у меня был негибкий процесс сборки в устаревшем приложении), и я просто тестирую в браузере, я обнаружил, что вы можете обойти захват $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); })); 

Если серьезно. Используйте Карму . Для настройки требуется небольшая работа, но она позволяет запускать все ваши тесты в нескольких браузерах одновременно из командной строки. Таким образом, вы можете использовать его как часть вашей системы непрерывной интеграции и / или вы можете сделать ее ярлыком из своего редактора. Гораздо лучше, чем alt-tab-refresh-ad-infinitum.

То, что я закончил, – это получить кеш шаблона и поместить в него представление. У меня нет контроля над использованием ngMock, получается:

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

Эта начальная проблема может быть решена путем добавления этого:

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

Это потому, что он пытается найти $ httpBackend в модуле ngMock по умолчанию, и он не заполнен.

Решение, которое я достиг, требует jasmine-jquery.js и прокси-сервера.

Я выполнил следующие шаги:

  1. В karma.conf:

добавить jasmine-jquery.js в свои файлы

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

добавьте прокси-сервер, который будет сервер ваших светильников

 proxies = { '/' : 'http://localhost:3502/' }; 
  1. В вашей спецификации

    описать (‘MySpec’, function () {var $ scope, template; jasmine.getFixtures (). fixturesPath = ‘public / partials /’; // настраиваемый путь, чтобы вы могли обслуживать реальный шаблон, который вы используете в приложении beforeEach (функция () {template = угловой.элемент (”);

      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. Запустите карму.

Мне потребовалось некоторое время, чтобы понять это, нужно искать много сообщений, я думаю, что документация об этом должна быть более четкой, так как это такая важная проблема.

Мое решение:

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(''); element = $compile(element)(scope); scope.$digest(); expect(element.html()).toContain('div'); })); }); 

Если вы используете Grunt, вы можете использовать угловые шаблоны. Он загружает ваши шаблоны в templateCache, и это непрозрачно для конфигурации ваших спецификаций.

Мой пример конфигурации:

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

Я решил ту же проблему несколько иначе, чем выбранное решение.

  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/' } 
  2. Затем я загрузил модуль, созданный в beforeEach. В вашем файле Spec.js:

     beforeEach(module('myApp', 'to/templates/myTemplate.html')); 
  3. Затем я использовал $ templateCache.get, чтобы сохранить его в переменной. В вашем файле Spec.js:

     var element, $scope, template; beforeEach(inject(function($rootScope, $compile, $templateCache) { $scope = $rootScope.$new(); element = $compile('
    ')($scope); template = $templateCache.get('to/templates/myTemplate.html'); $scope.$digest(); }));
  4. Наконец, я протестировал его таким образом. В вашем файле Spec.js:

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

Чтобы динамически загрузить шаблон html в $ templateCache, вы можете просто использовать предварительный процессор hmml2js karma, как описано здесь

это сводится к добавлению шаблонов « .html» к вашим файлам в файле conf.js, а также препроцессоры = {‘ .html’: ‘html2js’};

и использовать

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

в ваш тестовый файл js

если вы используете Karma, подумайте о том, как использовать предварительный компилятор karma-ng-html2js, чтобы предварительно скомпилировать внешние HTML-шаблоны и избежать использования метода Angular try для HTTP GET во время выполнения теста. Я боролся с этим для пары наших – в моем случае частичные пути шаблонаUrl, разрешенные во время обычного выполнения приложения, но не во время тестов, из-за различий в структурах приложений и тестовых dir.

Если вы используете jasmine-maven-plugin вместе с RequireJS, вы можете использовать текстовый плагин для загрузки содержимого шаблона в переменную, а затем поместить его в кеш шаблона.

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

Если вы используете 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(''); scope = $rootScope; $compile(element)(scope); scope.$digest(); })); }); }); 

Я разрешаю эту проблему с компиляцией всех шаблонов в templatecache. Я использую глоток, вы можете найти подобное решение для хрюка тоже. Мои шаблоны в директивах, модалях выглядят как

 `templateUrl: '/templates/directives/sidebar/tree.html'` 
  1. Добавьте новый пакет npm в мой пакет. Json

    "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. Добавить все js-файлы в index.html

  4. Наслаждайтесь!

  • Как расширить или переопределить существующие фильтры в angularjs?
  • есть ли обратная связь для рендеринга для Angular JS?
  • Использование ui-router с Bootstrap-ui modal
  • Почему устаревшие / ошибки метода AngularJS $ http не рекомендуются? Удалено из v1.6?
  • Угловая - разница между нетронутыми / грязными и коснутыми / нетронутыми
  • Как использовать $ on в сервисе в угловом режиме?
  • Угловой sanitize / ng-bind-html не работает?
  • Лучшие практики SPA для аутентификации и управления сеансами
  • Получение ошибки при использовании ng-controllerа в angularjs ver 1.3.0
  • AngularJs Удалите повторяющиеся элементы в ng-repeat
  • Как получить доступ к родительской области из пользовательской директивы * с помощью собственной области * в AngularJS?
  • Давайте будем гением компьютера.