首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >AngularJS承诺在JasmineJS测试中不触发回调

AngularJS承诺在JasmineJS测试中不触发回调
EN

Stack Overflow用户
提问于 2014-02-07 00:06:59
回答 3查看 14.7K关注 0票数 41

问题简介

我正在尝试对包装了AngularJS FB对象的Facebook JavaScript SDK服务进行单元测试;但是,测试不起作用,而且我还没有弄清楚原因。此外,当我在浏览器中运行服务代码而不是使用JasmineJS单元测试运行卡玛试验转轮时,服务代码也可以工作。

我正在测试通过$q对象使用角承诺的异步方法。我已经将测试设置为使用waitsFor()异步运行,但是函数永远不会返回(请参阅下面的测试代码),它只是在5秒之后超时。(Karma还没有附带Jasmin2.0异步测试API )。

我认为这可能是因为承诺的then()方法从来没有触发过(我已经设置了一个console.log()来证明这一点),尽管在异步方法返回时调用了$scope.$apply(),但是让角知道它应该运行摘要周期并触发可能出错的then() callback...but。

这是运行测试时产生的错误输出:

代码语言:javascript
运行
复制
Chrome 32.0.1700 (Mac OS X 10.9.1) service Facebook should return false
  if user is not logged into Facebook FAILED
  timeout: timed out after 5000 msec waiting for something to happen
Chrome 32.0.1700 (Mac OS X 10.9.1):
  Executed 6 of 6 (1 FAILED) (5.722 secs / 5.574 secs)

“守则”

这是我对服务的单元测试(参见内联注释,它解释了到目前为止所发现的):

代码语言:javascript
运行
复制
'use strict';

describe('service', function () {
  beforeEach(module('app.services'));

  describe('Facebook', function () {
    it('should return false if user is not logged into Facebook', function () {
      // Provide a fake version of the Facebook JavaScript SDK `FB` object:
      module(function ($provide) {
        $provide.value('fbsdk', {
          getLoginStatus: function (callback) { return callback({}); },
          init: function () {}
        });
      });

      var done = false;
      var userLoggedIn = false;

      runs(function () {
        inject(function (Facebook, $rootScope) {
          Facebook.getUserLoginStatus($rootScope)
            // This `then()` callback never runs, even after I call
            // `$scope.$apply()` in the service :(
            .then(function (data) {
              console.log("Found data!");
              userLoggedIn = data;
            })
            .finally(function () {
              console.log("Setting `done`...");
              done = true;
            });
        });
      });

      // This just times-out after 5 seconds because `done` is never
      // updated to `true` in the `then()` method above :(
      waitsFor(function () {
        return done;
      });

      runs(function () {
        expect(userLoggedIn).toEqual(false);
      });

    }); // it()
  }); // Facebook spec
}); // Service module spec

这是我正在测试的棱角服务(请参阅解释到目前为止所发现的内容的内联注释):

代码语言:javascript
运行
复制
'use strict';

angular.module('app.services', [])
  .value('fbsdk', window.FB)
  .factory('Facebook', ['fbsdk', '$q', function (FB, $q) {

    FB.init({
      appId: 'xxxxxxxxxxxxxxx',
      cookie: false,
      status: false,
      xfbml: false
    });

    function getUserLoginStatus ($scope) {
      var deferred = $q.defer();
      // This is where the deferred promise is resolved. Notice that I call
      // `$scope.$apply()` at the end to let Angular know to trigger the
      // `then()` callback in the caller of `getUserLoginStatus()`.
      FB.getLoginStatus(function (response) {
        if (response.authResponse) {
          deferred.resolve(true);
        } else {
          deferred.resolve(false)
        }
        $scope.$apply(); // <-- Tell Angular to trigger `then()`.
      });

      return deferred.promise;
    }

    return {
      getUserLoginStatus: getUserLoginStatus
    };
  }]);

资源

下面是我已经看过的解决这个问题的其他资源列表。

  • $q 这说明了如何在called中使用承诺,并给出了一个如何使用允诺的单元测试代码的示例(请注意为什么需要调用$scope.$apply()来触发then()回调)。
  • Jasmine异步测试示例
代码语言:javascript
运行
复制
- [Jasmine.Async: Making Asynchronous Testing With Jasmine Suck Less](http://lostechies.com/derickbailey/2012/08/18/jasmine-async-making-asynchronous-testing-with-jasmine-suck-less/)
- [Testing Asynchronous Javascript w/ Jasmine 2.0.0](http://blogs.lessthandot.com/index.php/webdev/uidevelopment/javascript/testing-asynchronous-javascript-w-jasmine/)

给出了如何使用Jasmin1.3.1异步方法测试实现承诺模式的对象的示例。它们与我在自己的测试中使用的模式略有不同,该模式是根据直接来自Jasmin1.3.1异步测试文档的示例建模的。

  • StackOverflow答案
代码语言:javascript
运行
复制
- Promise callback not called in Angular JS  
    - [Answer 1](https://stackoverflow.com/a/16323920/456814)
    - [Answer 2](https://stackoverflow.com/a/16323885/456814)

代码语言:javascript
运行
复制
- [angularjs - promise never resolved in controller](https://stackoverflow.com/a/14246907/456814)
- [AngularJS promises not firing when returned from a service](https://stackoverflow.com/a/14530341/456814)

摘要

请注意,我知道Facebook JavaScript SDK已经有其他的角库了,如下所示:

我现在对使用它们不感兴趣,因为我想学习如何自己编写一个有角度的服务。因此,请将的答案仅限于帮助我解决--我的代码中的问题,而不是建议我使用其他人的

这么说吧,,有人知道为什么我的测试不起作用吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-02-07 05:24:00

TL;博士

从您的测试代码中调用$rootScope.$digest(),它将通过:

代码语言:javascript
运行
复制
it('should return false if user is not logged into Facebook', function () {
  ...

  var userLoggedIn;

  inject(function (Facebook, $rootScope) {
    Facebook.getUserLoginStatus($rootScope).then(function (data) {
      console.log("Found data!");
      userLoggedIn = data;
    });

    $rootScope.$digest(); // <-- This will resolve the promise created above
    expect(userLoggedIn).toEqual(false);
  });
});

柱塞这里.

注意:我删除了run()wait()调用,因为这里不需要它们(没有执行实际的异步调用)。

长解释

下面是正在发生的事情:当您调用getUserLoginStatus()时,它在内部运行FB.getLoginStatus(),然后按其应有的方式执行回调FB.getLoginStatus(),因为您已经对它进行了精确的模拟。但是您的$scope.$apply()调用在该回调中,因此它在测试中的.then()语句之前执行。由于then()创建了一个新的承诺,为了解决这个问题,需要一个新的摘要。

我相信这个问题不会出现在浏览器中,原因有两个:

  1. FB.getLoginStatus()不会立即调用其回调,因此任何then()调用都会首先运行;或
  2. 应用程序中的其他内容会触发一个新的摘要周期。

因此,为了结束它,如果您在测试中显式地或不显式地创建一个承诺,您将不得不在某个时候触发一个摘要周期,以使该承诺得到解决。

票数 52
EN

Stack Overflow用户

发布于 2014-02-07 14:38:19

代码语言:javascript
运行
复制
    'use strict';

describe('service: Facebook', function () {
    var rootScope, fb;
    beforeEach(module('app.services'));
    // Inject $rootScope here...
    beforeEach(inject(function($rootScope, Facebook){
        rootScope = $rootScope;
        fb = Facebook;
    }));

    // And run your apply here
    afterEach(function(){
        rootScope.$apply();
    });

    it('should return false if user is not logged into Facebook', function () {
        // Provide a fake version of the Facebook JavaScript SDK `FB` object:
        module(function ($provide) {
            $provide.value('fbsdk', {
                getLoginStatus: function (callback) { return callback({}); },
                init: function () {}
            });
        });
        fb.getUserLoginStatus($rootScope).then(function (data) {
            console.log("Found data!");
            expect(data).toBeFalsy(); // user is not logged in
        });
    });
}); // Service module spec

这应该能做你想要的。通过使用beforeEach设置您的rootScope和afterEach来运行应用程序,您还可以轻松地扩展您的测试,这样您就可以为用户登录添加一个测试。

票数 5
EN

Stack Overflow用户

发布于 2014-07-19 07:43:54

从我可以看到的问题,为什么您的代码不工作,是您没有注入$scope。米希尔的回答是有效的,因为他注射$rootScope并调用消化周期。但是,您的$apply()是调用摘要周期的更高级别,所以它也会工作.但!只有当你把它注入服务本身。

但是我认为一个服务不会创建一个$scope子程序,所以您需要注入$rootScope本身--据我所知,只有控制器允许您将$scope作为创建一个$scope的工作。但这是一个小小的推测,我不能百分之百肯定。不过,我会尝试使用$rootScope,因为您知道这个应用程序有一个来自ng-app创建的$rootScope。

代码语言:javascript
运行
复制
'use strict';

angular.module('app.services', [])
  .value('fbsdk', window.FB)
  .factory('Facebook', ['fbsdk', '$q', '$rootScope' function (FB, $q, $rootScope) { //<---No $rootScope injection

    //If you want to use a child scope instead then --> var $scope = $rootScope.$new();
    // Otherwise just use $rootScope

    FB.init({
      appId: 'xxxxxxxxxxxxxxx',
      cookie: false,
      status: false,
      xfbml: false
    });

    function getUserLoginStatus ($scope) { //<--- Use of scope here, but use $rootScope instead
      var deferred = $q.defer();
      // This is where the deferred promise is resolved. Notice that I call
      // `$scope.$apply()` at the end to let Angular know to trigger the
      // `then()` callback in the caller of `getUserLoginStatus()`.
      FB.getLoginStatus(function (response) {
        if (response.authResponse) {
          deferred.resolve(true);
        } else {
          deferred.resolve(false)
        }
        $scope.$apply(); // <-- Tell Angular to trigger `then()`. USE $rootScope instead!
      });

      return deferred.promise;
    }

    return {
      getUserLoginStatus: getUserLoginStatus
    };
  }]);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21616766

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档