监视范围函数,该函数在初始化角度控制器时执行

时间:2014-09-24 20:06:41

标签: angularjs jasmine karma-runner

我想测试以下函数实际上是在使用jasmine初始化此控制器时调用的。似乎使用间谍是要走的路,它只是没有按照我期望的那样工作,因为我把它的预期称为“它”块。我想知道是否有一种特殊的方法可以检查是否在调用范围函数中调用了某些内容,而只是在控制器本身中调用。

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

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

    $scope.loadResponses();

}]);

// spec file

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

4 个答案:

答案 0 :(得分:9)

您需要使用您创建的范围自行初始化控制器。问题是,您需要重新构建代码。你不能监视一个不存在的函数,但是你需要在函数被调用之前进行spyOn。

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

由于您无法执行此操作,因此您需要在其他位置进行$scope.loadResponses()调用。

成功监视范围函数的代码是:

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

答案 1 :(得分:4)

在控制器实例化之前设置spy(在beforeEach中)是测试在实例化时执行的控制器函数的方法。

编辑:还有更多内容。正如评论指出的那样,在ctrl实例化时该函数不存在。要窥探该调用,您需要在具有范围之后在设置块中为变量分配一个任意函数(在这种情况下,您将scope.getResponses分配给空函数),但是在实例化控制器之前。然后你需要编写间谍(再次在你的设置块和BEFORE ctrl实例化),最后你可以实例化控制器并期望对该函数进行调用。对不起最初的糟糕答案

答案 2 :(得分:2)

我发现测试此类场景的唯一方法是将要测试的方法移动到单独的依赖项中,然后将其注入控制器中,并在测试中提供伪造。

这是一个非常基本的工作示例:

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

对于真实世界的代码,你可能需要额外的工作(例如,你可能并不总是想伪造loadResponses方法),但你明白了。

此外,这是一篇很好的文章,解释了如何创建实际使用Jasmine间谍的假依赖:Mocking Dependencies in AngularJS Tests

编辑:以下是另一种使用$provide.delegate并且不替换原始方法的方法:

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

答案 3 :(得分:0)

我不太明白上面的任何答案。

我经常使用的方法 - 不要测试它,而是测试它产生的输出..

你还没有指明loadResponses实际上做了什么......但是让我们说它放在范围内 - 所以测试它的存在..

顺便说一句 - 我自己问了一个类似的问题,但是在一个孤立的范围内 angular - how to test directive with isolatedScope load?

如果你还想要间谍 - 在一个无法隔离的范围内,你绝对可以使用一种技术..

例如,将代码更改为

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

 $scope.loadResponses();

这样您就可以在初始化控制器之前定义间谍。

另一种方式,就像PSL在评论中建议的那样 - 将loadResponses移到服务上,监视它并检查它是否已被调用。

然而,如上所述,这不适用于隔离范围..因此,测试其输出的方法是我真正推荐的唯一方法,因为它可以解决这两种情况。