web-dev-qa-db-fra.com

Fonction d'espionnage qui s'exécute lorsqu'un contrôleur angulaire est initialisé

Je veux tester que la fonction suivante est en fait appelée lors de l'initialisation de ce contrôleur à l'aide de jasmine. Il semble que l'utilisation d'un espion soit la solution. Cela ne fonctionne tout simplement pas comme je l'imaginais lorsque je m'attendais à ce qu'il soit appelé dans un blocage. Je me demande s'il existe un moyen spécial de vérifier si quelque chose a été appelé alors qu'elle ne l'était pas dans une fonction de scope, mais uniquement dans le contrôleur lui-même. 

 App.controller('aCtrl', [ '$scope', function($scope){

    $scope.loadResponses = function(){
        //do something
    }

    $scope.loadResponses();

}]);

// fichier de spécification

describe('test spec', function(){

    beforeEach(
    //rootscope assigned to scope, scope injected into controller, controller instantiation.. the expected stuff

        spyOn(scope, 'loadResponses');
    );

    it('should ensure that scope.loadResponses was called upon instantiation of the controller', function(){
         expect(scope.loadResponses).toHaveBeenCalled();
    });
});
18
Matt MacLeod

Définir l'espion avant l'instanciation du contrôleur (dans beforeEach) permet de tester les fonctions du contrôleur qui s'exécutent lors de l'instanciation.

EDIT: Il y a plus que ça. Comme le souligne un commentaire, la fonction n'existe pas au moment de l'instanciation ctrl. Pour espionner cet appel, vous devez affecter une fonction arbitraire à la variable (dans ce cas, vous affectez scope.getResponses à une fonction vide) dans votre bloc d'installation APRÈS que vous ayez l'étendue, mais AVANT d'instancier le contrôleur. Ensuite, vous devez écrire l’espion (à nouveau dans votre bloc d’installation et AVANT l’instanciation du contrôle), et vous pouvez enfin instancier le contrôleur et s’attendre à ce qu’un appel ait été effectué vers cette fonction. Désolé pour la réponse merdique au départ

4
Matt MacLeod

Vous devez initialiser vous-même le contrôleur avec l'étendue que vous avez créée. Le problème est que vous devez restructurer votre code. Vous ne pouvez pas espionner une fonction non existante, mais vous devez spyOn avant que la fonction ne soit appelée.

$scope.loadResponses = function(){
    //do something
}
// <-- You would need your spy attached here
$scope.loadResponses();

Comme vous ne pouvez pas faire cela, vous devez faire l'appel $scope.loadResponses() ailleurs.

Le code qui espionnerait avec succès une fonction étendue est le suivant:

var scope;
beforeEach(inject(function($controller, $rootScope) {
    scope = $rootScope.$new();
    $controller('aCtrl', {$scope: scope});
    scope.$digest();
}));
it("should have been called", function() {
    spyOn(scope, "loadResponses");
    scope.doTheStuffThatMakedLoadResponsesCalled();
    expect(scope.loadResponses).toHaveBeenCalled();
});
9
Zenorbi

Le seul moyen que j'ai trouvé pour tester ce type de scénario consiste à déplacer la méthode à tester vers une dépendance distincte, puis à l'injecter dans le contrôleur et à fournir un faux dans les tests.

Voici un exemple de travail très basique:

angular.module('test', [])
    .factory('loadResponses', function() {
        return function() {
            //do something
        }
    })
    .controller('aCtrl', ['$scope', 'loadResponses', function($scope, loadResponses) {
        $scope.loadResponses = loadResponses;

        $scope.loadResponses();
    }]);

describe('test spec', function(){
    var scope;
    var loadResponsesInvoked = false;

    var fakeLoadResponses = function () {
        loadResponsesInvoked = true;
    }

    beforeEach(function () {
        module('test', function($provide) {
            $provide.value('loadResponses', fakeLoadResponses)
        });

        inject(function($controller, $rootScope) {
            scope = $rootScope.$new();
            $controller('aCtrl', { $scope: scope });
        });
    });

    it('should ensure that scope.loadResponses was called upon instantiation of the controller', function () {
        expect(loadResponsesInvoked).toBeTruthy();
    });
});

Pour du code du monde réel, vous aurez probablement besoin de travail supplémentaire (par exemple, vous ne voudrez peut-être pas toujours simuler la méthode loadResponses), mais vous avez l’idée.

En outre, voici un article de Nice qui explique comment créer de fausses dépendances utilisant réellement Jasmine Spies: Mocking Dependencies in AngularJS Tests

EDIT: Voici une méthode alternative, qui utilise $provide.delegate et ne remplace pas la méthode d'origine:

describe('test spec', function(){
    var scope, loadResponses;
    var loadResponsesInvoked = false;

    beforeEach(function () {
        var loadResponsesDecorator = function ($delegate) {
            loadResponsesInvoked = true;
            return $delegate;
        }

        module('test', function($provide) {
            $provide.decorator('loadResponses', loadResponsesDecorator);
        });

        inject(function($controller, $rootScope) {
            scope = $rootScope.$new();
            $controller('aCtrl', { $scope: scope });
        });
    });

    it('should ensure that scope.loadResponses was called upon instantiation of the controller', function () {
        expect(loadResponsesInvoked).toBeTruthy();
    });
});
2
Konamiman

Je n'ai pas bien compris les réponses ci-dessus. 

la méthode que j'utilise souvent - ne le testez pas, mais le résultat qu'il génère. 

vous n'avez pas précisé ce que loadResponses fait réellement .. mais disons qu'il met quelque chose sur la portée - testez donc l'existence de cela .. 

BTW - J'ai moi-même posé une question similaire mais sur un champ d'application isolé angulaire - comment tester une directive avec une charge isolatedScope?

si vous voulez toujours espionner - sur un oscilloscope non isolé, vous pouvez certainement utiliser une technique .. 

par exemple, modifiez votre code pour qu'il soit 

 if ( !$scope.loadResponses ){
     $scope.loadResponses = function(){}
 } 

 $scope.loadResponses();

De cette façon, vous pourrez définir l'espion avant d'initialiser le contrôleur. 

Une autre façon, c'est comme PSL suggéré dans les commentaires - déplacez loadResponses vers un service, espionnez-le et vérifiez qu'il a été appelé. 

Cependant, comme mentionné, cela ne fonctionnera pas sur une portée isolée .. et donc la méthode de test de la sortie est la seule que je recommande vraiment, car elle répond aux deux scénarios. 

0
guy mograbi