wake-up-neo.com

Spy-on-Scope-Funktion, die ausgeführt wird, wenn eine Winkelsteuerung initialisiert wird

Ich möchte testen, dass die folgende Funktion tatsächlich bei der Initialisierung dieses Controllers mit Jasmin aufgerufen wird. Es scheint, als wäre ein Spion der Weg zu gehen. Es funktioniert einfach nicht so, wie ich es mir vorgestellt hätte, wenn ich die Erwartung an den "it" -Block nehme. Ich frage mich, ob es eine spezielle Möglichkeit gibt, zu überprüfen, ob etwas aufgerufen wurde, als es nicht innerhalb einer Bereichsfunktion, sondern nur im Controller selbst aufgerufen wurde. 

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

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

    $scope.loadResponses();

}]);

// Spezifikationsdatei

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

Durch Einstellen des Spions vor der Instantiierung der Controller (in beforeEach) können Sie die Controller-Funktionen testen, die bei der Instantiierung ausgeführt werden.

EDIT: Es gibt mehr dazu. Ein Kommentar weist darauf hin, dass die Funktion zum Zeitpunkt der Strg-Instanziierung nicht vorhanden ist. Um diesen Aufruf auszuspionieren, müssen Sie der Variablen eine beliebige Funktion zuweisen (in diesem Fall weisen Sie einer leeren Funktion scope.getResponses zu), nachdem Sie den Gültigkeitsbereich haben, aber bevor Sie den Controller instanziieren. Dann müssen Sie den Spion schreiben (wieder in Ihrem Setup-Block und VOR der CTRL-Instantiierung), und schließlich können Sie den Controller instanziieren und erwarten, dass ein Aufruf an diese Funktion erfolgt ist. Entschuldigung für die beschissene Antwort

4
Matt MacLeod

Sie müssen den Controller selbst mit dem erstellten Bereich initialisieren. Das Problem ist, dass Sie Ihren Code umstrukturieren müssen. Sie können keine nicht vorhandene Funktion ausspionieren, aber Sie müssen spyOn aktivieren, bevor die Funktion aufgerufen wird.

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

Da Sie dies nicht tun können, müssen Sie den Aufruf von $scope.loadResponses() an anderer Stelle durchführen.

Der Code, der erfolgreich eine Bereichsfunktion ausspionieren würde, lautet wie folgt:

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

Die einzige Möglichkeit, diese Art von Szenarien zu testen, besteht darin, die zu testende Methode in eine separate Abhängigkeit zu verschieben, sie dann in die Steuerung zu injizieren und stattdessen eine Fälschung in den Tests bereitzustellen.

Hier ist ein sehr einfaches Arbeitsbeispiel:

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

Für Code aus der realen Welt benötigen Sie wahrscheinlich zusätzliche Arbeit (z. B. möchten Sie möglicherweise nicht immer die loadResponses-Methode fälschen), aber Sie haben die Idee.

Hier ist auch ein Nizza-Artikel, in dem erläutert wird, wie falsche Abhängigkeiten erstellt werden, die Jasmin-Spione tatsächlich verwenden: Spottende Abhängigkeiten in AngularJS-Tests

EDIT: Hier ist ein alternativer Weg, der $provide.delegate verwendet und die ursprüngliche Methode nicht ersetzt:

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

Ich habe keine der obigen Antworten verstanden. 

die Methode, die ich häufig verwende - testen Sie sie nicht, testen Sie stattdessen die Ausgabe, die sie erzeugt. 

sie haben nicht angegeben, was loadResponses tatsächlich tut. Aber sagen wir mal, es setzt etwas in den Geltungsbereich. 

BTW - Ich habe selbst eine ähnliche Frage gestellt, aber in einem isolierten Bereich Winkel - wie man Direktiven mit isolierter Scope-Last testet?

wenn Sie trotzdem ausspionieren möchten, können Sie auf unisolierte Bereiche durchaus eine Technik anwenden. 

Ändern Sie beispielsweise Ihren Code in 

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

 $scope.loadResponses();

Auf diese Weise können Sie den Spion definieren, bevor Sie den Controller initialisieren. 

Ein anderer Weg ist wie PSL in den Kommentaren vorgeschlagen - verschieben Sie loadResponses zu einem Dienst, spionieren Sie das aus und überprüfen Sie, ob es aufgerufen wurde. 

Wie bereits erwähnt, funktioniert dies jedoch nicht in einem isolierten Bereich. Daher ist die Methode zum Testen der Ausgabe die einzige, die ich wirklich empfehle, da beide Szenarien beantwortet werden. 

0
guy mograbi