所以我没有看到另一个问题:
How to mock required directive controller in directive UT这基本上是我的问题,但似乎这个线程的答案是“改变你的设计。我想确保没有办法做到这一点。我有一个指令,声明一个控制器被儿童指令使用。我现在试图写儿童指令的茉莉花测试,但我不能让他们在测试中编译,因为他们依赖于控制器。这是它的外观:
addressModule.directive('address',['$http',function($http){ return { replace: false,restrict: 'A',scope: { config: '=' },template: '<div id="addressContainer">' + '<div ng-if="!showAddressSelectionPage" basic-address config="config"/>' + '<div ng-if="showAddressSelectionPage" address-selector addresses="standardizedAddresses"/>' + '</div>',controller: function($scope) { this.showAddressInput = function(){ $scope.showAddressSelectionPage = false; }; this.showAddressSelection = function(){ $scope.getStandardizedAddresses(); }; this.finish = function(){ $scope.finishAddress(); }; },link: function(scope,element,attrs) { ... } } }])
儿童指示:
addressModule.directive('basicAddress360',['translationService',function(translationService){ return { replace: true,template: '...',require: "^address360",attrs,addressController){ ... } } }])
茉莉花试验:
it("should do something",inject(function($compile,$rootScope){ parentHtml = '<div address/>'; subDirectiveHtml = '<div basic-address>'; parentElement = $compile(parentHtml)(rootScope); parentScope = parentElement.scope(); directiveElement = $compile(subDirectiveHtml)(parentScope); directiveScope = directiveElement.scope(); $rootScope.$digest(); }));
有没有办法让我用茉莉花测试sub指令,如果是这样,我错过了什么?即使我可以测试指令本身没有控制器的功能,我会很高兴。
我可以想到两种方法:
1)使用这两个指令
让我们假设我们有以下指令:
app.directive('foo',function() { return { restrict: 'E',controller: function($scope) { this.add = function(x,y) { return x + y; } } }; }); app.directive('bar',require: '^foo',foo) { scope.callFoo = function(x,y) { scope.sum = foo.add(x,y); } } }; });
为了测试callFoo方法,你可以简单地编译这两个指令,让bar使用foo的实现:
it('ensures callFoo does whatever it is supposed to',function() { // Arrange var element = $compile('<foo><bar></bar></foo>')($scope); var barScope = element.find('bar').scope(); // Act barScope.callFoo(1,2); // Assert expect(barScope.sum).toBe(3); });
2)模拟foo的控制器
这个不是很直接,有点棘手。你可以使用element.controller()获取元素的控制器,并用Jasmine模拟它:
it('ensures callFoo does whatever it is supposed to',function() { // Arrange var element = $compile('<foo><bar></bar></foo>')($scope); var fooController = element.controller('foo'); var barScope = element.find('bar').scope(); spyOn(fooController,'add').andReturn(3); // Act barScope.callFoo(1,2); // Assert expect(barScope.sum).toBe(3); expect(fooController.add).toHaveBeenCalledWith(1,2); });
当一个指令在其链接函数中立即使用另一个控制器时,棘手的部分出现:
app.directive('bar',foo) { scope.sum = foo.add(parseInt(attrs.x),parseInt(attrs.y)); } }; });
在这种情况下,您需要单独编译每个指令,以便在第二个使用它之前模拟第一个指令:
it('ensures callFoo does whatever it is supposed to',function() { // Arrange var fooElement = $compile('<foo></foo>')($scope); var fooController = fooElement.controller('foo'); spyOn(fooController,'add').andReturn(3); var barElement = angular.element('<bar x="1" y="2"></bar>') fooElement.append(barElement); // Act barElement = $compile(barElement)($scope); var barScope = barElement.scope(); // Assert expect(barScope.sum).toBe(3); expect(fooController.add).toHaveBeenCalledWith(1,2); });
第一种方法比第二种方法更容易,但它依赖于第一个指令的实现,即,你不是单元测试的东西。另一方面,虽然嘲笑指令的控制器不是那么容易,它给你更多的控制测试和删除对第一个指令的依赖。所以,选择明智。