2014-09-24 55 views
18

我想测试下面的函数实际上是在使用茉莉花初始化此控制器时调用的。看起来使用间谍是一种可行的方式,它只是不像我预期的那样工作,当我把期望称为它在一个'它'块。我想知道是否有一种特殊的方法来检查是否在范围函数内调用了某些东西时被调用,但仅在控制器本身中。窥视角度控制器初始化时执行的示波器功能

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

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

    $scope.loadResponses(); 

}]); 

//规范文件

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

它通常会工作,但不会在这种情况下。因为你在控制器被实例化并调用了方法之后设置了间谍。即使你在作用域上创建了一个间谍方法,它也会在控制器被实例化时被覆盖,并且它不会真正起作用。 – PSL 2014-09-24 20:16:28

+0

当我想到它时,你不需要测试初始化​​时是否调用了'$ scope.loadResponses'。因为如果你的控制器实例化成功(没有它,反正你的测试都不会通过),所以保证'$ scope.loadResponses'已被调用。所以我没有看到在初始化函数上进行间谍活动。 – PSL 2014-09-24 20:49:28

+1

在路上,我不希望开发人员将其删除(有意或无意),因为它获得了使用该页面的关键资源。我能够检查一个变量是否在初始化后被分配了预期的值,我有点惊讶,我无法使用间谍来检查初始化调用。 – 2014-09-24 21:09:23

回答

4

设置控制器实例化之前,间谍(在beforeEach)是测试在实例化时,其执行控制器功能的方式。

编辑:还有更多。正如注释所指出的那样,该函数在ctrl实例化时不存在。为了监视这个调用,你需要在你有范围之后在你的设置块中为变量赋予一个任意函数(在这种情况下,你将scope.getResponses赋给一个空函数),但是在你实例化控制器之前。然后你需要写入间谍(再次在你的设置块和之前的ctrl实例化),最后你可以实例化控制器,并期望对该函数进行调用。对不起,最初的蹩脚答案

+1

Zenorbi的答案有更多关于如何正确设置它的详细信息,但这是正确的答案。在Zenorbi的beforeEach中调用'scope。$ digest'之前,用'spyOn(scope,'loadResponses')'设置你的间谍,你应该状态良好。 – 2015-09-10 14:33:10

+4

这对我没有任何意义..需要示例。在init之前,函数不存在 - 所以没什么可窥探的。如果我们事先指定了间谍,它将在init中被覆盖。 – 2015-10-05 23:40:37

+0

@guymograbi我已更新答案以更好地解释...以前的答案是误导性的,我很抱歉。 – 2016-01-05 18:27:17

9

你需要自己与你所创建的范围初始化控制器。问题是,你需要重构你的代码。你不能窥探一个不存在的函数,但你需要在函数被调用之前侦听。

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

在控制器初始化中调用loadResponses。这就是问题的全部,因为我明白'我想测试下面的函数实际上是否需要初始化这个控制器'。这似乎是鸡蛋<->鸡蛋的情况。 – PSL 2014-09-24 20:38:15

0

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

的方法我经常使用 - 不进行测试,而不是测试它使输出..

你没有指定什么loadResponses实际上做..但让说,它把一些关于范围 - 所以测试存在那..

顺便说一句 - 我自己也问过类似的问题,但在一个偏僻的范围 angular - how to test directive with isolatedScope load?

如果你仍然想窥探 - 上未分离的范围,你可以肯定使用的技术..

例如,改变你的代码

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

$scope.loadResponses(); 

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

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

但是,如上所述,这不会在一个孤立的范围内工作..所以测试它的输出的方法是我真正推荐的唯一一个,因为它回答了两种情况。

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法)现实世界中的代码,但你的想法。

而且,这里是一个很好的文章,介绍如何创建一个实际使用茉莉花间谍假依赖关系: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(); 
    }); 
});