【问题标题】:Angular.js promise not resolving when unit testing service with karma当使用业力对服务进行单元测试时,Angular.js 承诺不会解决
【发布时间】:2014-06-24 03:52:58
【问题描述】:

我正在尝试对 Angular.js 服务进行单元测试,并且需要对从 Mock 服务返回的承诺设置期望(使用 Jasmine)。我正在使用业力单元测试框架。相关代码sn-p如下:

// I can't figure out how to do the equivalent of a $scope.$digest here. 
var loginStatusPromise = FacebookService.getFacebookToken();
loginStatusPromise.then(function(token) {
    expect(false).toBeTruthy(); // If this test passes, there is something going wrong!
    expect(token).not.toBeNull(); // The token should be ValidToken
    expect(token).toBe('ValidToken');
});

完整的单元测试代码可见here

问题是 promise.then 语句在 karma 执行时永远不会触发。因此,我的期望语句都没有被执行。 在我的控制器测试中,我使用 $scope.$digest() 来解决承诺,但我不清楚如何在服务测试中执行此操作。正如我认为在服务测试中没有“范围”的概念。

我这里有错误的一端吗?我是否需要将 $rootScope 注入我的服务测试然后使用 $digest?或者,还有其他方法吗?

【问题讨论】:

  • 对我的回答有任何反馈吗?
  • 是的,见下文。调用 $rootScope.$digest() 似乎也不起作用。这里还有别的东西在起作用。更新代码的链接如下所示。

标签: javascript angularjs unit-testing karma-runner


【解决方案1】:

我遇到了这个问题,只需添加一个 $rootScope.$apply() 在我的测试结束时

您的 FacebookService 可能是问题所在,正如 @mpm 所建议的那样。您确定它在 Facebook 依赖项中没有发生任何在单元测试期间不会发生的 http 调用吗?你确定resolve 已经被延迟调用了吗?

【讨论】:

  • $rootScope.$apply() 放在promise.resolved() 之后对我有类似的帮助。 AngularJS 承诺似乎只在 $digest 循环的下一次迭代中触发,在这样的单元测试中可能不会自动触发。
【解决方案2】:

假设您正在使用ngFacebook/ngModule 在解决方案/想法之前快速说明该项目没有单元测试!你确定要使用这个项目吗?

我在 Github 上快速浏览了您的单元测试,发现以下缺失:-

1) 模块初始化。 ngFacebook 需要它,或者您需要初始化执行相同操作的模块。

beforeEach(module('ngFacebook'));

beforeEach(module('yieldtome'));

2) 认真考虑模拟 ngFacebook 模块

在单元级测试中,您是在一个模拟气泡中测试您的代码,其中外部接口被剔除。

否则)尝试添加调用API如下:-

 $rootScope.$apply(function() {
     this.FacebookService.getFacebookToken().then(function(){
        //your expect code here
     });
    });
 $httpBackend.flush();//mock any anticipated outgoing requests as per [$httpBackend][2]

【讨论】:

    【解决方案3】:
    beforeEach(function(){
       var self=this;
        inject(function($rootScope,Facebook){
            self.$rootScope=$rootScope;
            self.Facebook=Facebook;
        });
    })
    
    it('resolves unless sourcecode broken',function(done){
        // I can't figure out how to do the equivalent of a $scope.$digest here. 
        var loginStatusPromise = this.FacebookService.getFacebookToken();
        loginStatusPromise.then(function(token) {
            expect(token).toBe('ValidToken');
            done();
        });
        $rootscope.$apply();
    
    });
    

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

    【讨论】:

    • 不幸的是,这种方法也不起作用。然后函数未调用,未命中。
    • 如果它被破坏了——那么它会以这样的方式被破坏,它可以在真实的应用程序中工作,但只有在作为单元测试/在业力中运行时才会破坏。 FacebookService 代码可以在here 看到,如果你想查看其他文件,你也可以在那里浏览其余的 repo
    • 关于 $rootScope 的整件事有点错误(不管它是否有效)。服务的单元测试不应该知道关于控制器范围的任何信息。 $q 以某种方式与 $rootScope 交互的事实不可能是有效的关注点分离。
    【解决方案4】:

    我同意上面的回答,服务应该与$rootScope无关。

    在我的例子中,有一个$q 承诺,它使用第二个服务在内部解析为一个承诺。没有办法解决外部问题,除非我将$rootScope.$digest() 添加到我的服务代码(不是测试)中......

    我最终为 $q 编写了这个快速 shim 以在我的测试中使用,但要小心,因为它只是一个示例,而不是完整的 $q 实现。

    beforeEach(module(function($provide) {
      $provide.value('$q', {
        defer: function() {
          var _resolve, _reject;
          return {
            promise: {
              then: function (resolve, reject) {
                _resolve = resolve;
                _reject = reject;
              }
            },
            resolve: function (data) {
              window.setTimeout(_resolve, 0, data);
            },
            reject: function (data) {
              window.setTimeout(_reject, 0, data);
            }
          };
        }
      });
    }));
    

    希望它对某人有用,或者如果您有任何反馈。

    谢谢。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-30
      • 2013-02-09
      • 2019-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-25
      相关资源
      最近更新 更多