因此,我确实看到了另一个问题:如何在指令UT中模拟所需的指令控制器,这基本上是我的问题,但似乎该线程的答案是“更改设计”。我想确保没有办法做到这一点。我有一个指令声明一个由子指令使用的控制器。我现在正尝试为children指令编写茉莉花测试,但由于它们依赖于控制器,因此我无法让它们在测试中进行编译。看起来是这样的:
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, restrict: 'A', scope: { config: '=' }, template: '...', require: "^address360", link: function(scope, element, 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(); }));
我没有办法用茉莉花测试子指令吗?如果可以,我会缺少什么?即使我可以在没有控制器功能的情况下测试指令本身,我也会很高兴。
我可以想到两种方法:
1)同时使用两个指令
假设我们有以下指令:
app.directive('foo', function() { return { restrict: 'E', controller: function($scope) { this.add = function(x, y) { return x + y; } } }; }); app.directive('bar', function() { return { restrict: 'E', require: '^foo', link: function(scope, element, attrs, foo) { scope.callFoo = function(x, y) { scope.sum = foo.add(x, y); } } }; });
为了测试该callFoo方法,您可以简单地编译两个指令并bar使用use foo的实现:
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对其进行模拟:
element.controller()
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); });
当一个指令在其link功能中立即使用另一个控制器时,就会出现棘手的部分:
link
app.directive('bar', function() { return { restrict: 'E', require: '^foo', link: function(scope, element, attrs, 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); });
第一种方法比第二种方法容易得多,但是它依赖于第一个指令的实现,即您没有对单元进行测试。另一方面,尽管模拟指令的控制器不是那么容易,但是它可以让您更好地控制测试并消除对第一个指令的依赖。因此,明智地选择。:)
最后,我不知道执行上述所有操作的简便方法。如果有人知道更好的方法,请改善我的答案。