57 votes

Comment écrire des tests des contrôleurs avec des méthodes privées dans AngularJs?

Bon, j'ai été de tomber sur un problème depuis longtemps et j'aimerais entendre l'avis du reste de la communauté.

Tout d'abord, examinons quelques abstrait contrôleur.

function Ctrl($scope, anyService) {

   $scope.field = "field";
   $scope.whenClicked = function() {
      util();
   };

   function util() {
      anyService.doSmth();
   }

}

Il est clair que nous avons ici:

  • régulière échafaudage pour contrôleur $scope et certains services injecté
  • sur le terrain, et de la fonction attachée à la portée
  • méthode privée util()

Maintenant, j'aimerais couvrir cette classe de tests unitaires (Jasmine). Cependant, le problème est que je veux vérifier que lorsque je clique sur (appelez - whenClicked()) un élément que l' util() méthode sera appelée. Je ne sais pas comment le faire, puisque, dans le Jasmin tests je suis toujours avoir des erreurs que soit la fantaisie pour util() n'a pas été défini ou n'a pas été appelé.

Note: je ne suis pas d'essayer de corriger cet exemple particulier, je parle de test, par exemple du modèle de code en général. Donc merci de ne pas me dire "ce qui est exact de l'erreur". Je me demande comment faire, pas comment résoudre ce problème.

J'ai essayé un certain nombre de façons de contourner cela:

  • évidemment je ne peux pas utiliser $scope dans mes tests d'unité que je n'ai pas cette fonction attachée à cet objet (il se termine généralement avec message d' Expected spy but got undefined ou similaire)
  • J'ai essayé de l'attacher à ces fonctions, le contrôleur de l'objet via Ctrl.util = util; puis de vérifier se moque comme d' Ctrl.util = jasmine.createSpy() mais dans ce cas - Ctrl.util n'est pas appelée si les tests échouent
  • J'ai essayé de changer de util() à être attaché this objet et se moquant Ctrl.util encore une fois, avec pas de chance

Eh bien, je ne peux pas trouver mon chemin autour de cela, je m'attends de l'aide de JS ninjas, un travail de violon, ce serait parfait.

41voto

yianis Points 141

La fonction de contrôleur que vous avez fournis seront utilisés par Anguleux comme un constructeur; à un certain moment, il sera appelé avec new pour créer le réel de l'instance du contrôleur. Si vous avez vraiment besoin de fonctions dans votre contrôleur de l'objet qui ne sont pas exposés à la somme de portée, mais qui sont disponibles pour l'espionnage/cogner/se moquant de vous pourrait attacher this.

function Ctrl($scope, anyService) {

  $scope.field = "field";
  $scope.whenClicked = function() {
    util();
  };

  this.util = function() {
    anyService.doSmth();
  }
}

Lorsque vous appelez var ctrl = new Ctrl(...) ou de l'utilisation de l'Angulaire $controller service pour récupérer l' Ctrl de l'instance, l'objet retourné contiendra l' util fonction.

Vous pouvez le voir, cette approche ici: http://jsfiddle.net/yianisn/8P9Mv/

31voto

MikeMac Points 183

Namespacing sur l'étendue de la pollution. Ce que vous voulez faire est de s'extraire de cette logique dans une fonction distincte qui est ensuite injectée dans votre Contrôleur. c'est à dire

function Ctrl($scope, util) {

   $scope.field = "field";
   $scope.whenClicked = function() {
      util();
   };
}

angular.module("foo", [])
       .service("anyService", function(...){...})
       .factory("util", function(anyService) {
              return function() {
                     anyService.doSmth();
              };
       });

Maintenant, vous pouvez unité de test avec se moque de votre Ctrl ainsi que "util".

2voto

digger69 Points 2084

Nous avons couru dans cette ainsi et a commencé à l'aide de cette approche: il suffit de noms de ces fonctions en vertu d'un membre privé sur le contrôleur;

function Ctrl($scope, anyService) {

   $scope.field = "field";
   $scope.whenClicked = function() {
      $scope.private.util();
   };

   $scope.private = {
     util: function() {
      anyService.doSmth();
     }
   }

}

et dans notre contrôleur de tests d'ajouter des montres/se moque de $champ d'application.privé.util, etc.

spyOn( scope.private, 'util');

2voto

ŁukaszBachman Points 10541

Je suis en ajoutant une réponse contenant mon approche actuelle, l'espoir d'obtenir quelques commentaires et peut-être l'éclat de discussion au sujet de si oui ou non c'est une bonne solution.

Nous attachons, privé des fonctions à la fonction de contrôleur (et donc de faire leur public, ce qui permet de se moquant). Pour éviter d'avoir à répéter le nom de contrôleur de tous les temps et de faire de la syntaxe plus attrayant, nous créons self objet qui contient la référence à la fonction de contrôleur. Il devient donc:

function Ctrl($scope, anyService) {

   $scope.field = "field";
   $scope.whenClicked = function() {
      self.util();
   };

   var self = Ctrl; // For the sake of syntax simplicity only

   self.util = function() {
      anyService.doSmth();
   };

}

et puis dans les tests unitaires maintenant, nous pouvons utiliser:

Ctrl.util = jasmine.createSpy("util()");
expect(Ctrl.util).toHaveBeenCalled();

Je n'aime pas beaucoup cela, mais je pense que c'est la façon la plus simple de le faire. J'espère que quelqu'un va trouver la meilleure approche.

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