如果没有$ scope强制摘要,如何解决AngularJS,Jasmine 2.0中的promise?

时间:2014-06-03 17:07:57

标签: angularjs jasmine karma-runner karma-jasmine

似乎承诺do not resolve in Angular/Jasmine tests unless you force a $scope.$digest()。这是愚蠢的IMO,但很好,我在适用的地方工作(控制器)。

我现在的情况是我有一个服务,它可以不关心应用程序中的任何作用域,它只是从服务器返回一些数据,但承诺似乎并没有解决

app.service('myService', function($q) {
  return {
    getSomething: function() {
      var deferred = $q.defer();
      deferred.resolve('test');
      return deferred.promise;
    }
  }
});

describe('Method: getSomething', function() {
  // In this case the expect()s are never executed
  it('should get something', function(done) {
    var promise = myService.getSomething();

    promise.then(function(resp) {
      expect(resp).toBe('test');      
      expect(1).toEqual(2);
    });

    done();
  });

  // This throws an error because done() is never called.
  // Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
  it('should get something', function(done) {
    var promise = myService.getSomething();

    promise.then(function(resp) {
      expect(resp).toBe('test');      
      expect(1).toEqual(2);
      done();
    });
  });
});

测试此功能的正确方法是什么?

编辑:供参考的解决方案。显然,即使服务没有使用它,你也不得不注入和消化$ rootScope。

  it('should get something', function($rootScope, done) {
    var promise = myService.getSomething();

    promise.then(function(resp) {
      expect(resp).toBe('test');      
    });

    $rootScope.$digest();
    done();
  }); 

4 个答案:

答案 0 :(得分:41)

您需要在测试中注入$rootScope并在其上触发$digest

答案 1 :(得分:12)

总是有$ rootScope,使用它

inject(function($rootScope){
myRootScope=$rootScope;
})
....

myRootScope.$digest();

答案 2 :(得分:3)

所以我整个下午都在努力。看完这篇文章之后,我也觉得答案中有些东西;事实证明有。上述答案均未明确说明使用$rootScope.$digest的位置和原因。所以,这就是我想出来的。

首先为什么?每当您从非角度事件或回调响应时,您都需要使用$rootScope.$digest。这将包括纯DOM事件,jQuery事件以及$q以外的其他第三方Promise库,它们是角度的一部分。

其次在哪里?在你的代码中,不是你的测试。无需在您的测试中注入$rootScope,只需在您的实际角度服务中使用它。这就是所有上述内容都无法弄清楚答案是什么,他们将$rootScope.$digest显示为从测试中调用。

我希望这有助于下一个长期存在同样问题的人。

更新

昨天我被删除了这篇文章。今天我继续遇到这个问题,尝试使用上面提到的慷慨解答。所以,我以声誉点为代价来等待我的答案,因此,我取消了它。

这是您在非角度事件处理程序中所需要的,并且您正在使用$ q并尝试使用Jasmine进行测试。

something.on('ready', function(err) {
    $rootScope.$apply(function(){deferred.resolve()});              
});
  

请注意,在某些情况下可能需要将其包装在$ timeout中。

something.on('ready', function(err) {
    $timeout(function(){
      $rootScope.$apply(function(){deferred.resolve()});    
    });     
});
  

还有一点需要注意。在原始问题示例中,您在错误的时间调用done。在结算后,您需要在承诺的done方法(或thencatch)内调用finally。您在promise解析之前调用它,这导致it子句终止。

答案 3 :(得分:3)

来自角度文档。

https://docs.angularjs.org/api/ng/service/ $ Q

it('should simulate promise', inject(function($q, $rootScope) {
  var deferred = $q.defer();
  var promise = deferred.promise;
  var resolvedValue;

  promise.then(function(value) { resolvedValue = value; });
  expect(resolvedValue).toBeUndefined();

  // Simulate resolving of promise
  deferred.resolve(123);
  // Note that the 'then' function does not get called synchronously.
  // This is because we want the promise API to always be async, whether or not
  // it got called synchronously or asynchronously.
  expect(resolvedValue).toBeUndefined();

  // Propagate promise resolution to 'then' functions using $apply().
  $rootScope.$apply();
  expect(resolvedValue).toEqual(123);
}));