122 votes

Test unitaire de la directive AngularJS avec templateUrl

J'ai un AngularJS directive qui a un templateUrl défini. Je suis en train de test de l'unité avec le Jasmin.

Mon Jasmin JavaScript ressemble à la suivante, conformément à la recommandation de la ce:

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

        });
    });
});

Lorsque je l'exécute dans mon Jasmin spec erreur que j'obtiens l'erreur suivante:

TypeError: Object #<Object> has no method 'passThrough'

Tout ce que je veux est pour le templateUrl à être chargé comme - je ne veux pas utiliser respond. Je crois que cela peut être lié à l'aide de ngMock au lieu de ngMockE2E. Si c'est le coupable, comment puis-je utiliser ce dernier à la place de l'ancien?

Merci à l'avance!

186voto

SleepyMurph Points 541

Vous avez raison que c'est lié à ngMock. Le ngMock module est automatiquement chargé pour chaque Angulaire de test, et il initialise la maquette $httpBackend pour gérer toute utilisation de l' $http service, qui inclut le modèle de l'extraction. Le système de template essaie de charger le template par $http et il devient un "inattendu de la demande" à la maquette.

Ce que vous avez besoin d'un moyen de pré-charger les modèles dans l' $templateCache alors qu'ils sont déjà disponibles lorsque Angulaire de la demande, sans l'aide d' $http.

La Solution Préférée: Karma

Si vous êtes à l'aide de Karma pour exécuter vos tests (et vous devriez), vous pouvez le configurer pour charger les modèles pour vous avec le ng-html2js de préprocesseur. Ng-html2js lit les fichiers HTML que vous spécifiez et les convertit en angle module de pré-charge le $templateCache.

Étape 1: Activer et configurer le préprocesseur dans votre 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 vous utilisez Yeoman à l'échafaud votre application de cette config va travailler

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

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

ngHtml2JsPreprocessor: { 
  stripPrefix: 'app/', 
  moduleName: 'my.templates' 
},

Étape 2: Utiliser le module dans vos tests

// my-test.js

beforeEach(module("my.templates"));    // load new module containing templates

Pour un exemple complet, regardez cet exemple canonique Angulaires test gourou Vojta Jina. Il comprend un ensemble d'installation: karma config, des modèles et des tests.

Un Non-Karma Solution

Si vous n'utilisez pas de Karma, pour quelque raison que ce soit (la paresse, de la bêtise, inflexible processus de construction dans l'ancienne application, etc) et sont tout simplement de le tester dans un navigateur, j'ai trouvé que vous pouvez obtenir autour de ngMock prise de $httpBackend à l'aide d'un raw XHR pour aller chercher le modèle réel et de l'insérer dans l' $templateCache. Cette solution est beaucoup moins flexible, mais il fait le travail pour l'instant.

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

Sérieusement, si. L'Utilisation De Karma.

37voto

Words Like Jared Points 3290

Ce que j’ai finalement fait, c’est d’obtenir le cache de modèles et d’y insérer la vue. Je n'ai pas le contrôle sur le fait de ne pas utiliser ngMock, il s'avère:

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

13voto

bullgare Points 793

Ce problème initial peut être résolu en ajoutant ceci:

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

C'est parce qu'il essaie de trouver $ httpBackend dans le module ngMock par défaut et que ce n'est pas complet.

8voto

canotto90 Points 1552

Enfin, il a travaillé!!

La solution que j'ai atteint besoins jasmine-jquery.js et un serveur proxy.

J'ai suivi cette procédure:

1) Dans le karma.conf:

ajouter jasmine-jquery.js pour vos fichiers

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

ajouter un serveur proxy qui va le serveur de vos appareils

proxies = {
    '/' : 'http://localhost:3502/'
};

2) Dans votre spec

describe('MySpec', function() {
    var $scope, template;
    jasmine.getFixtures().fixturesPath = 'public/partials/'; //custom path so you can serve the real template you use on the app
    beforeEach(function() {
        template = angular.element('<my-directive-name></my-directive-name>');

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

3) tourner un serveur sur votre app du répertoire racine

python -m SimpleHTTPServer 3502

4) Exécuter le karma.

Il a pris mon temps pour comprendre cela, avoir à chercher sur de nombreux posts, je pense que la documentation à ce sujet doit être plus clair, que c'est une question de cette importance.

7voto

bartek Points 413

Ma solution:

test/karma-utils.js :

 function httpGetSync(filePath) {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/base/app/" + filePath, false);
  xhr.send();
  return xhr.responseText;
}
function preloadTpl(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'
],
 

le test:

 'use strict';
describe('Directive: gowiliEvent', function () {
  // load the directive's module
  beforeEach(module('frontendSrcApp'));
  var element,
    scope;
  beforeEach(preloadTpl('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');
  }));
});
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X