66 votes

Contrôler les directives de test unitaire dans Angular sans rendre le contrôleur global

Dans Vojta Jina excellent référentiel dans lequel il démontre l'essai des directives, il définit la directive contrôleur de l'extérieur du module wrapper. Voir ici: https://github.com/vojtajina/ng-directive-testing/blob/master/js/tabs.js

N'est-ce pas une mauvaise pratique et de polluer l'espace de noms global?

Si l'on devait avoir un autre endroit où il pourrait être logique d'appeler quelque chose TabsController, ne serait-ce pas casser des trucs?

Les tests de la directive est d'être trouvé ici: https://github.com/vojtajina/ng-directive-testing/commit/test-controller

Est-il possible de tester la directive contrôleurs séparés du reste de la directive, sans placer le contrôleur dans un espace de noms global?

Il serait agréable d'encapsuler l'ensemble de la directive au sein de l'application.la directive(...) la définition.

75voto

James van Dyke Points 653

Je préfère de temps en temps pour inclure mon controller avec la directive j'ai donc besoin d'un moyen de le tester.

D'abord la directive

angular.module('myApp', [])
  .directive('myDirective', function() {
    return {
      restrict: 'EA',
      scope: {},
      controller: function ($scope) {
        $scope.isInitialized = true
      },
      template: '<div>{{isInitialized}}</div>'
    }
})

Les tests:

describe("myDirective", function() ->
  var el, scope, controller;

  beforeEach inject(function($compile, $rootScope) {
    # Instantiate directive.
    # gotacha: Controller and link functions will execute.
    el = angular.element("<my-directive></my-directive>")
    $compile(el)($rootScope.$new())
    $rootScope.$digest()

    # Grab controller instance
    controller = el.controller()

    # Grab scope. Depends on type of scope.
    # See angular.element documentation.
    scope = el.isolateScope() || el.scope() 
  })

  it("should do something to the scope", function() {
    expect(scope.isInitialized).toBeDefined()
  })
})

Voir angulaire.élément de la documentation pour plus de moyens pour obtenir des données à partir d'un instancié de la directive.

Attention, l'instanciation de la directive implique que le contrôleur et toutes les fonctions de liaison auront déjà courir, de sorte que pourrait affecter vos tests.

58voto

pkozlowski.opensource Points 52557

Excellente question!

Donc, c'est un souci commun, non seulement avec les contrôleurs, mais aussi, potentiellement, avec les services qu'une directive pourriez avoir besoin pour effectuer son travail, mais n'avez pas forcément envie d'exposer ce contrôleur / service pour le "monde extérieur".

Je crois fermement que les données globales sont mauvais et doivent être évitées et cela s'applique à la directive des contrôleurs. Si nous prenons cette hypothèse, nous pouvons prendre plusieurs approches différentes pour définir les contrôleurs "localement". Tout en faisant de sorte que nous devons garder à l'esprit qu' un contrôleur devrait être encore "facilement" accessible aux tests unitaires , donc nous ne peut tout simplement cacher dans la directive de la fermeture. OMI possibilités sont:

1) tout d'Abord, nous pourrions simplement définir la directive du contrôleur sur un module de niveau, ex::

angular.module('ui.bootstrap.tabs', [])
  .controller('TabsController', ['$scope', '$element', function($scope, $element) {
    ...
  }])
 .directive('tabs', function() {
  return {
    restrict: 'EA',
    transclude: true,
    scope: {},
    controller: 'TabsController',
    templateUrl: 'template/tabs/tabs.html',
    replace: true
  };
})

C'est une technique simple qui nous aide dans https://github.com/angular-ui/bootstrap/blob/master/src/tabs/tabs.js qui est basé sur Vojta.

Alors que c'est une technique très simple, il convient de noter qu'un contrôleur est toujours exposé à l'ensemble de l'application qui signifie que d'autres module pourrait éventuellement le remplacer. En ce sens, il rend un contrôleur local à AngularJS application (afin de ne pas polluer une fenêtre globale portée) mais aussi global pour tous les AngularJS modules.

2) Utiliser une fermeture de la portée et spécial des fichiers de configuration pour le test.

Si nous voulons cacher complètement un contrôleur de fonction on peut encapsuler le code dans une clôture. C'est une technique qui AngularJS. Par exemple, en regardant la NgModelController nous pouvons voir qu'il est défini comme un "global" en fonction de ses propres fichiers (et donc facilement accessible pour les tests) mais le fichier entier est enveloppé dans de la fermeture pendant le temps de construction:

Pour résumer: l'option (2) est "plus sûr", mais nécessite un peu de configuration pour le construire.

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