Ich habe eine AngularJS-Direktive, für die ein templateUrl
definiert ist. Ich versuche es mit Jasmine zu testen.
Mein Jasmin JavaScript sieht wie folgt aus, laut Empfehlung von this :
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');
});
});
});
});
Wenn ich dies in meinem Jasmine-Spezifikationsfehler ausführe, erhalte ich den folgenden Fehler:
TypeError: Object #<Object> has no method 'passThrough'
Ich möchte nur, dass die templateUrl so geladen wird, wie sie ist - ich möchte respond
nicht verwenden. Ich glaube, dies könnte damit zusammenhängen, dass ngMock anstelle von ngMockE2E verwendet wird. Wenn dies der Schuldige ist, wie verwende ich den letzteren anstelle des ersteren?
Danke im Voraus!
Am Ende habe ich den Vorlagen-Cache abgerufen und die Ansicht dort eingefügt. Ich habe keine Kontrolle darüber, ob ich ngMock nicht benutze.
beforeEach(inject(function(_$rootScope_, _$compile_, $templateCache) {
$scope = _$rootScope_;
$compile = _$compile_;
$templateCache.put('path/to/template.html', '<div>Here goes the template</div>');
}));
Sie haben Recht, dass es mit ngMock zusammenhängt. Das ngMock-Modul wird automatisch für jeden Angular Test) geladen und initialisiert den Schein $httpBackend
, Um jede Verwendung des $http
- Dienstes, der das Abrufen von Vorlagen umfasst, zu verarbeiten. Das Template-System versucht, das Template über $http
Zu laden und es wird zu einer "unerwarteten Anfrage" an den Mock.
Was Sie brauchen, ist eine Möglichkeit, die Vorlagen in den $templateCache
Vorzuladen, damit sie bereits verfügbar sind, wenn Angular nach ihnen fragt, ohne $http
Zu verwenden.
Wenn Sie Karma verwenden, um Ihre Tests auszuführen (und das sollten Sie auch tun), können Sie es so konfigurieren, dass die Vorlagen mit dem Präprozessor ng-html2js geladen werden. Ng-html2js liest die von Ihnen angegebenen HTML-Dateien und konvertiert sie in ein Angular Modul, das den $templateCache
Vorlädt.
Schritt 1: Aktivieren und konfigurieren Sie den Präprozessor in Ihrem 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"
},
Wenn Sie Yeoman verwenden, um Ihre App zu rüsten, funktioniert diese Konfiguration
plugins: [
'karma-phantomjs-launcher',
'karma-jasmine',
'karma-ng-html2js-preprocessor'
],
preprocessors: {
'app/views/*.html': ['ng-html2js']
},
ngHtml2JsPreprocessor: {
stripPrefix: 'app/',
moduleName: 'my.templates'
},
Schritt 2: Verwenden Sie das Modul in Ihren Tests
// my-test.js
beforeEach(module("my.templates")); // load new module containing templates
Ein vollständiges Beispiel finden Sie hier Kanonisches Beispiel von Angular Testguru Vojta Jina . Es enthält ein vollständiges Setup: Karma-Konfiguration, Vorlagen und Tests.
Wenn Sie Karma aus irgendeinem Grund nicht verwenden (ich hatte einen unflexiblen Build-Prozess in einer Legacy-App) und teste nur in einem Browser, habe ich festgestellt, dass Sie die Übernahme von $httpBackend
Durch ngMock mit einem rohen XHR umgehen können um die Vorlage abzurufen und in das Feld $templateCache
einzufügen. Diese Lösung ist viel weniger flexibel, erledigt aber vorerst die Arbeit.
// 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);
}));
Aber im Ernst. Verwenden Sie Karma . Die Einrichtung ist ein wenig aufwändig, aber Sie können alle Ihre Tests in mehreren Browsern gleichzeitig über die Befehlszeile ausführen. Sie können es also als Teil Ihres kontinuierlichen Integrationssystems verwenden und/oder es als Tastenkürzel in Ihrem Editor verwenden. Viel besser als Alt-Tab-Refresh-Ad-Infinitum.
Dieses anfängliche Problem kann gelöst werden, indem Folgendes hinzugefügt wird:
beforeEach(angular.mock.module('ngMockE2E'));
Das liegt daran, dass versucht wird, $ httpBackend im ngMock Modul zu finden und es ist nicht voll.
Die Lösung, die ich erreicht habe, benötigt jasmine-jquery.js und einen Proxy-Server.
Ich bin diesen Schritten gefolgt:
fügen Sie jasmine-jquery.js zu Ihren Dateien hinzu
files = [
JASMINE,
JASMINE_ADAPTER,
...,
jasmine-jquery-1.3.1,
...
]
fügen Sie einen Proxy-Server hinzu, der Ihre Fixtures verwaltet
proxies = {
'/' : 'http://localhost:3502/'
};
In Ihrer Spezifikation
describe ('MySpec', function () {var $ scope, template; jasmine.getFixtures (). fixturesPath = 'public/partials /'; // benutzerdefinierter Pfad, damit Sie die echte Vorlage bereitstellen können, die Sie in der App vor jeder Funktion verwenden () {template = angle.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();
})
});
});
Führen Sie einen Server im Stammverzeichnis Ihrer App aus
python -m SimpleHTTPServer 3502
Run Karma.
Es hat eine Weile gedauert, bis ich herausgefunden hatte, dass ich viele Beiträge durchsuchen musste. Ich denke, die Dokumentation darüber sollte klarer sein, da es sich um ein so wichtiges Thema handelt.
Meine Lösung:
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'
],
der Test:
'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');
}));
});
Ich habe das gleiche Problem auf eine etwas andere Weise gelöst als die gewählte Lösung.
Zuerst habe ich das ng-html2js Plugin für Karma installiert und konfiguriert. In der Datei 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/'
}
Dann habe ich das in beforeEach erstellte Modul geladen. In Ihrer Spec.js-Datei:
beforeEach(module('myApp', 'to/templates/myTemplate.html'));
Dann habe ich $ templateCache.get verwendet, um es in einer Variablen zu speichern. In Ihrer Spec.js-Datei:
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();
}));
Zum Schluss habe ich es so getestet. In Ihrer Spec.js-Datei:
describe('element', function() {
it('should contain the template', function() {
expect(element.html()).toMatch(template);
});
});
Wenn Sie Grunt verwenden, können Sie Grunt-Angular-Templates verwenden. Es lädt Ihre Vorlagen in den templateCache und ist transparent für Ihre Spezifikationskonfiguration.
Meine Beispielkonfiguration:
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');
};
Um die HTML-Vorlage dynamisch in $ templateCache zu laden, können Sie einfach den HTML2J-Karma-Vorprozessor verwenden, wie in hier erklärt
dies läuft darauf hinaus, Vorlagen '. html' zu Ihren Dateien in der Datei conf.js sowie Präprozessoren = {'. html': 'html2js' hinzuzufügen. };
und verwenden
beforeEach(module('..'));
beforeEach(module('...html', '...html'));
in deine js testing datei
Wenn Sie in Ihren Tests requirejs verwenden, können Sie das 'text'-Plugin verwenden, um die HTML-Vorlage in den $ templateCache zu laden.
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();
}));
});
});
Wenn Sie das jasmine-maven-plugin zusammen mit RequireJS verwenden, können Sie das text plugin verwenden, um den Vorlageninhalt in eine Variable zu laden und ihn dann in den Vorlagencache zu legen.
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);
}));
});
});
wenn Sie Karma verwenden, ziehen Sie die Verwendung von karma-ng-html2js-preprocessor in Betracht, um Ihre externen HTML-Vorlagen vorzukompilieren, und vermeiden Sie, dass Angular versucht, sie während der Testausführung mit HTTP GET abzurufen. Ich hatte einige Probleme damit - in meinem Fall wurden die Teilpfade von templateUrl während der normalen App-Ausführung aufgelöst, aber nicht während der Tests - aufgrund von Unterschieden in den App- und Testverzeichnisstrukturen.
Ich behebe dieses Problem, indem ich alle Vorlagen in templatecache kompiliere. Ich benutze gulp, du kannst auch eine ähnliche Lösung für grunzen finden. Meine templateUrls in Direktiven, Modals sieht aus wie
`templateUrl: '/templates/directives/sidebar/tree.html'`
Füge ein neues npm-Paket in meine package.json hinzu
"gulp-angular-templatecache": "1.*"
In gulp file füge templatecache und eine neue Aufgabe hinzu:
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')); });
Fügen Sie alle js-Dateien in index.html hinzu
<script src="/assets/js/lib.js"></script> <script src="/assets/js/app.js"></script> <script src="/assets/js/templates.js"></script>
Genießen!