如何测试指令的link函数中的行为

时间:2012-11-15 21:25:11

标签: javascript angularjs jasmine

在我的一些指令中,我正在向作用域添加函数来处理指令的特定逻辑。例如:

link: function(scope, element, attrs) {
         scope.doStuff = function() {
            //do a bunch of stuff I want to test
         }        
      }

如何测试该功能?我搜索了如何测试指令,但是the things I found更多的是关于测试元素的更改。我可以在每次测试之前编译我的指令,但这样每次都会消灭我的范围。我想测试该函数作为我的范围更改中的属性。

有没有办法获取从指令定义返回的对象?然后我可以直接调用链接函数并测试作用域上定义的每个函数的行为。有没有更好的方法来做这一切?

我正在使用Jasmine来运行我的测试,我想在describe函数中设置我的作用域,因此我可以为相同的作用域数据提供多个it函数。

5 个答案:

答案 0 :(得分:44)

基本上,您不是测试链接函数本身,而是以编程方式测试指令的结果。你要做的是将指令写出一个字符串,并使用$compile对它进行角度处理。然后测试输出以确保所有内容都正确连接。

Angular的来源充满了如何做到这一点的好例子......例如Angular's test of the ngRepeat directive

您可以看到他们正在做的是设置指令,更改范围(在本例中为$rootScope),确保它是$digest,然后测试它输出的DOM以确保一切都正确连线。如果指令正在改变,那么你也可以测试范围内的内容。

The test for ngClick也非常有趣,因为它显示了浏览器交互的测试及其对范围的影响。

为了完整起见,这里是ngClick测试的一个片段,我认为总结得很好地测试了一个指令:

 it('should get called on a click', inject(function($rootScope, $compile) {
   element = $compile('<div ng-click="clicked = true"></div>')($rootScope);
   $rootScope.$digest();
   expect($rootScope.clicked).toBeFalsy();

   browserTrigger(element, 'click');
   expect($rootScope.clicked).toEqual(true);
 }));

所以对于你的scope.doStuff函数,我不会测试做什么,就像我测试它在范围上受到的影响一样,它随后会受到影响DOM元素。

答案 1 :(得分:2)

如果需要,可以直接对指令的链接方法进行单元测试。 请参阅角冰模块的单元测试仪:“测试指令配置”

http://bverbist.github.io/angular-ice/#/unitTester

示例用法:https://github.com/bverbist/angular-ice/blob/master/app/components/icebank/bank-account-number-directive_link_test.js

在您的情况下,您可以保留对传递给指令的链接方法的范围对象的引用,然后您可以直接测试该范围上的doStuff函数。

答案 2 :(得分:1)

我解决了这个问题的方式有点不同。

如果你的指令中有一个非常简单的链接函数,并且你不需要第三个参数( attrs ),只需删除链接函数并指定一个控制器而不是。

app.directive('loadIndicator', function() {
  return {
    restrict: 'E',
    replace: true,
    templateUrl: 'blahblah/indicator.html',
    controller: 'LoadIndicatorController'
  };
});

正如你在指令的链接函数中有范围和元素的args一样,这两个args可以作为 $ scope $ element <注入一个易于测试的控制器中/强>

如果您能够创建控制器并对这些控制器进行单元测试,那么这将非常容易。

答案 3 :(得分:0)

正如对@sqlexception的评论回复中所述。你真的需要掌握指令的范围,这并不难。您不想做的是修改您的代码以满足您的测试,因为它应该是相反的方式。

要获得指令的范围,只需编译:

var element = $compile(<directive's html>).($scope)

其中$scope$scope = $rootScrope.$new()一起声明。现在我们可以通过element.scope()

获得隔离范围

我最近写了一篇关于此内容的简短博文Testing a Linking Function  有一个帮助者:

答案 4 :(得分:0)

 it('should get called on a click', inject(function($rootScope, $compile) {
   var scope = $rootScope.$new();
   element = $compile('<div ng-click="doIt()"></div>')(scope);
   scope.$digest();
   expect(scope.$$childHead.doIt()).toBeDefined();
 }));

使用这个$$ childHead是我解决同一问题的解决方案,因此,我可以涵盖测试中未调用的功能。