Единичное тестирование Директива 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, я получаю следующую ошибку:
- Захват HTTP 401 с помощью перехватчика Angular.js
- Перенаправление на определенный маршрут на основе условия
- Получение «Несоответствующий модуль анонимного определения () ...», когда я пытаюсь выполнить тесты
- Лучшая практика использования $ rootcope в приложении Angularjs?
- Лучший способ представить сетку или таблицу в AngularJS с помощью Bootstrap 3?
TypeError: Object # has no method 'passThrough'
Все, что я хочу, это загрузить templateUrl как есть – я не хочу использовать respond
. Я считаю, что это может быть связано с ним, используя ngMock вместо ngMockE2E . Если это преступник, как использовать последний вместо первого?
Заранее спасибо!
- AngularJS + JQuery: как заставить динамический контент работать в angularjs
- Загрузка файла с помощью Angularjs вызывает redirect маршрутизатора
- Предоставление директив в $ sce.trustAsHtml
- В чем разница между $ routeProvider и $ stateProvider?
- получить исходный элемент из ng-click
- Параметры Angularjs ng с использованием номера для модели не выбирают начальное значение
- угловой ng-bind-html и директива внутри него
- Укажите пример события $ destroy в области видимости?
Вы правы, что это связано с 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 и прокси-сервера.
Я выполнил следующие шаги:
- В karma.conf:
добавить jasmine-jquery.js в свои файлы
files = [ JASMINE, JASMINE_ADAPTER, ..., jasmine-jquery-1.3.1, ... ]
добавьте прокси-сервер, который будет сервер ваших светильников
proxies = { '/' : 'http://localhost:3502/' };
-
В вашей спецификации
описать (‘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(); }) });
});
-
Запустите сервер в корневом каталоге приложения
python -m SimpleHTTPServer 3502
-
Запустите карму.
Мне потребовалось некоторое время, чтобы понять это, нужно искать много сообщений, я думаю, что документация об этом должна быть более четкой, так как это такая важная проблема.
Мое решение:
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'); };
Я решил ту же проблему несколько иначе, чем выбранное решение.
-
Во-первых, я установил и настроил плагин 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. В вашем файле Spec.js:
beforeEach(module('myApp', 'to/templates/myTemplate.html'));
-
Затем я использовал $ 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(); }));
-
Наконец, я протестировал его таким образом. В вашем файле 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'`
-
Добавьте новый пакет npm в мой пакет. Json
"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')); });
-
Добавить все js-файлы в index.html
-
Наслаждайтесь!