2014-09-24 53 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(); 
    }); 
});