angularjs - unit - testing with jasmine
Unit Testing AngularJS directive con templateUrl (12)
Este problema inicial puede resolverse agregando esto:
beforeEach(angular.mock.module(''ngMockE2E''));
Eso es porque trata de encontrar $ httpBackend en el módulo ngMock de manera predeterminada y no está lleno.
Tengo una directiva AngularJS que tiene una templateUrl
definida. Estoy intentando probarlo con Jasmine.
Mi jazmín JavaScript se parece a lo siguiente, según la recomendación de 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'');
});
});
});
});
Cuando ejecuto esto en mi error de especificación de Jasmine, aparece el siguiente error:
TypeError: Object #<Object> has no method ''passThrough''
Todo lo que quiero es que templateUrl se cargue como está; no quiero usar respond
. Creo que esto puede estar relacionado con el uso de ngMock lugar de ngMockE2E . Si este es el culpable, ¿cómo uso este último en lugar del anterior?
¡Gracias por adelantado!
La solución que llegué necesita jasmine-jquery.js y un servidor proxy.
Seguí estos pasos:
- En karma.conf:
agrega jasmine-jquery.js a tus archivos
files = [
JASMINE,
JASMINE_ADAPTER,
...,
jasmine-jquery-1.3.1,
...
]
agrega un servidor proxy que servirá tus accesorios
proxies = {
''/'' : ''http://localhost:3502/''
};
En tu especificación
describe (''MySpec'', function () {var $ scope, template; jasmine.getFixtures (). fixturesPath = ''public / partials /''; // ruta personalizada para que pueda servir la plantilla real que usa en la aplicación beforeEach (función () {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(); }) });
});
Ejecute un servidor en el directorio raíz de su aplicación
python -m SimpleHTTPServer 3502
Ejecutar karma
Me tomó un tiempo resolver esto, al tener que buscar muchos mensajes, creo que la documentación sobre esto debería ser más clara, ya que es un tema tan importante.
Lo que terminé haciendo fue obtener el caché de la plantilla y poner la vista allí. No tengo control sobre no usar ngMock, resulta que:
beforeEach(inject(function(_$rootScope_, _$compile_, $templateCache) {
$scope = _$rootScope_;
$compile = _$compile_;
$templateCache.put(''path/to/template.html'', ''<div>Here goes the template</div>'');
}));
Mi solución:
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''
],
la prueba:
''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'');
}));
});
Para cargar la plantilla html dinámicamente en $ templateCache, puedes usar el preprocesador karma html2js, como se explica here
esto se reduce a agregar plantillas '' .html'' a sus archivos en el archivo conf.js y preprocesadores = {'' .html'': ''html2js''};
y use
beforeEach(module(''..''));
beforeEach(module(''...html'', ''...html''));
en su archivo de prueba js
Resolví el mismo problema de una manera ligeramente diferente a la solución elegida.
Primero, instalé y configuré el complemento ng-html2js para karma. En el archivo 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/'' }
Luego cargué el módulo creado en beforeEach. En su archivo Spec.js:
beforeEach(module(''myApp'', ''to/templates/myTemplate.html''));
Luego usé $ templateCache.get para almacenarlo en una variable. En su archivo 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(); }));
Finalmente, lo probé de esta manera. En su archivo Spec.js:
describe(''element'', function() { it(''should contain the template'', function() { expect(element.html()).toMatch(template); }); });
Resuelvo este problema al compilar todas las plantillas a templatecache. Estoy usando gulp, también puedes encontrar una solución similar para gruñir. Mi templateUrls en directivas, modales parece
`templateUrl: ''/templates/directives/sidebar/tree.html''`
Agregue un nuevo paquete npm en mi paquete. Json
"gulp-angular-templatecache": "1.*"
En gulp file, agrega templatecache y una nueva tarea:
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'')); });
Agregue todos los archivos js en index.html
<script src="/assets/js/lib.js"></script> <script src="/assets/js/app.js"></script> <script src="/assets/js/templates.js"></script>
¡Disfrutar!
Si está usando Karma, considere usar ng-html2js para ng-html2js sus plantillas HTML externas y evitar que Angular intente HTTP GET durante la ejecución de la prueba. Luché con esto por un par de las nuestras, en mi caso las rutas parciales de templateUrl se resolvieron durante la ejecución normal de la aplicación, pero no durante las pruebas, debido a las diferencias en las estructuras de la aplicación frente a las estructuras de prueba.
Si está utilizando Grunt, puede usar grunt-angular-templates. Carga sus plantillas en templateCache y es transparente para la configuración de sus especificaciones.
Mi configuración de muestra:
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'');
};
Si está utilizando el jasmine-maven-plugin junto con RequireJS, puede usar el complemento de texto para cargar el contenido de la plantilla en una variable y luego ponerlo en la memoria caché de la plantilla.
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);
}));
});
});
Si usa requirejs en sus pruebas, puede usar el complemento ''text'' para extraer la plantilla html y colocarla en $ 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();
}));
});
});
Tiene razón en que está relacionado con ngMock. El módulo ngMock se carga automáticamente para cada prueba angular e inicializa $httpBackend
simulado para manejar cualquier uso del servicio $http
, que incluye la búsqueda de plantillas. El sistema de plantillas intenta cargar la plantilla a través de $http
y se convierte en una "solicitud inesperada" para el simulacro.
Lo que necesita es una forma de precargar las plantillas en $templateCache
para que estén disponibles cuando Angular las solicite, sin usar $http
.
La solución preferida: Karma
Si está utilizando Karma para ejecutar sus pruebas (y debería hacerlo), puede configurarlo para que cargue las plantillas con el preprocesador ng-html2js . Ng-html2js lee los archivos HTML que especifica y los convierte en un módulo angular que carga previamente el $templateCache
.
Paso 1: habilite y configure el preprocesador en su 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"
},
Si está utilizando Yeoman para andamiar su aplicación, esta configuración funcionará
plugins: [
''karma-phantomjs-launcher'',
''karma-jasmine'',
''karma-ng-html2js-preprocessor''
],
preprocessors: {
''app/views/*.html'': [''ng-html2js'']
},
ngHtml2JsPreprocessor: {
stripPrefix: ''app/'',
moduleName: ''my.templates''
},
Paso 2: usa el módulo en tus pruebas
// my-test.js
beforeEach(module("my.templates")); // load new module containing templates
Para un ejemplo completo, mire este ejemplo canónico del gurú de pruebas angulares Vojta Jina . Incluye una configuración completa: configuración de karma, plantillas y pruebas.
Una solución sin karma
Si no usa Karma por alguna razón (tuve un proceso de compilación inflexible en la aplicación heredada) y solo estoy probando en un navegador, he descubierto que puede evitar la toma de control de $httpBackend
por $httpBackend
al usar un XHR sin formato para recuperar la plantilla de verdad e insertarlo en $templateCache
. Esta solución es mucho menos flexible, pero hace el trabajo por el momento.
// 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);
}));
Hablando en serio. Usa Karma Lleva un poco de esfuerzo configurarlo, pero le permite ejecutar todas sus pruebas, en varios navegadores a la vez, desde la línea de comandos. Entonces puede tenerlo como parte de su sistema de integración continua, y / o puede convertirlo en una tecla de acceso directo de su editor. Mucho mejor que alt-tab-refresh-ad-infinitum.