2014-01-09 70 views
0

我遇到過一個我無法通過測試的情況。如何在angularjs中測試承諾

下面是測試:

it('should accept an fucntion run it immediately', inject(function($rootScope, ready) { 
    var spy = jasmine.createSpy('ready').andCallFake(function(){ 
     console.log("I'm ready"); 
    }); 
    ready.done(spy); 
    expect(spy).toHaveBeenCalled(); 
})); 

這裏是代碼:

angular.module('myApp').factory('ready', function($q, _, $rootScope){ 

    var defer = $q.defer(); 

    //in real case it's actually calling 3rd party code, so no $timeout 
    _.defer(function(){ 
     $rootScope.$apply(function(){ 
      defer.resolve(); 
     }); 
    }); 

    return { 
     done: function(fn){ 
      defer.promise.then(fn); 
     } 
    }; 
}); 

I'm ready日誌沒有出現,但測試還是失敗了。所以我認爲它只是在jasmine中處理異步流的問題。但我想不出用茉莉花的runswaitsFor來測試它。

+0

看到這個答案:http://stackoverflow.com/questions/15477370/unit-testing-an-asynchronous-service-in-angularjs –

+0

一個可能的簡化:爲什麼不直接從工廠返回'defer.promise'?然後調用代碼可以使用'ready.then(function(){...})',否則你會稍微回到回調地獄。 –

+0

@MichalCharemza那會很好 – jackysee

回答

1

我懷疑測試失敗,然後按照該順序出現console.log,因爲直到_.defer調用它的回調(即當前調用堆棧被清除後)才能解決承諾。

您需要的是強制下劃線/ lodash _.defer的方法,以調用其回調/承諾,以便您的工廠承諾立即得到解決。如果你使用的是$timeout,你可以在測試中調用$timeout.flush(),但據我所知,下劃線/ lodash沒有這樣的東西。

然而,在測試之前,你應該能夠注入模擬「_」,具有延遲功能,調用它的直接回調:

beforeEach(module(function($provide) { 
    $provide.value('_', { 
    'defer': function(callback) {callback()}; 
    });  
})); 

在你的真實情況下,你需要做的像上面這樣,通過注入一個模擬的第三方服務並強制它完成,所以你的承諾在測試中的expect調用之前得到解決。

如果因爲某些原因無法注入第三方代碼的模擬版本,可以使測試異步,如http://pivotal.github.io/jasmine/#section-Asynchronous_Support所述,但應儘可能避免這種情況,因爲這會使測試運行速度變慢。

+0

它不起作用,我猜是因爲$ apply是異步? – jackysee

+0

只有在期望檢查之前添加$ rootScope。$ apply()纔有效。 – jackysee

+0

你是對的!我剛剛[做了這個Plunker](http://plnkr.co/edit/LPsuH5I6J0BsjRVdKEvD?p=preview)來解決這個問題 –