2014-01-09 73 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)来解决这个问题 –