AngularJS无极回调JasmineJS试验未触发时 [英] AngularJS Promise Callback Not Trigged in JasmineJS Test

查看:150
本文介绍了AngularJS无极回调JasmineJS试验未触发时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图单元测试封装了 Facebook上AngularJS服务
JavaScript的SDK
FB 对象;然而,测试不工作,
我一直无法找出原因。此外,该服务code呢
工作时,我在浏览器中,而不是 JasmineJS 单位运行
测试中,与噶测试运行运行。

I'm trying to unit test an AngularJS service that wraps the Facebook JavaScript SDK FB object; however, the test isn't working, and I haven't been able to figure out why. Also, the service code does work when I run it in a browser instead of a JasmineJS unit test, run with Karma test runner.

我测试通过 $ Q
目的。我已经使用茉莉花设置为异步运行测试
1.3.1异步测试方法
的,但 waitsFor()
函数永远不会返回真正(见测试code以下),它只是将超时
5秒钟后
。 (噶不与茉莉花2.0异步API测试尚未出货)。

I'm testing an asynchronous method using Angular promises via the $q object. I have the tests set up to run asynchronously using the Jasmine 1.3.1 async testing methods, but the waitsFor() function never returns true (see test code below), it just times-out after 5 seconds. (Karma doesn't ship with the Jasmine 2.0 async testing API yet).

我想可能是因为在则()方法
承诺永远不会触发(我有一个的console.log()设置为显示
那),即使我打电话 $范围。$适用()当异步
方法返回,让角知道它应该运行摘要
循环并触发则()回调...但我可能是错的。

I think it might be because the then() method of the promise is never triggered (I've got a console.log() set up to show that), even though I'm calling $scope.$apply() when the asynchronous method returns, to let Angular know that it should run a digest cycle and trigger the then() callback...but I could be wrong.

这是来自运行测试的错误输出:

This is the error output that comes from running the test:

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)

的code

这是我的服务单元测试(看到,解释一下我到目前为止发现内嵌注释):

'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

这是正在测试我的角度服务(看到,解释一下我到目前为止发现内嵌注释):

'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
    };
  }]);

资源

下面是我在已经采取了看其他资源列表
试图解决这个问题。

Resources

Here is a list of other resources that I've already taken a look at to try to solve this problem.

这说明了如何使用承诺的角度,以及给予使用承诺如何单元测试code的例子(注意,为什么 $范围的解释。$适用() 需要调用触发则()回调)。

This explains how to use promises in Angular, as well as giving an example of how to unit-test code that uses promises (note the explanation of why $scope.$apply() needs to be called to trigger the then() callback).

茉莉花异步测试实例

这给出了如何使用茉莉花1.3.1异步方法来测试实现无极模式对象的示例。他们是从我在我自己的测试,这是该示例仿照使用的模式直接从茉莉花自带略有不同1.3.1异步测试文档

These give examples of how to use the Jasmine 1.3.1 async methods to test objects implementing the Promise pattern. They're slightly different from the pattern I used in my own test, which is modeled after the example that comes directly from the Jasmine 1.3.1 async testing documentation.

StackOverflow的答案

StackOverflow Answers


  • 无极回调角JS不叫
    • Promise callback not called in Angular JS
      • Answer 1
      • Answer 2

      请注意,我知道,已经有其他的角图书馆在那里为Facebook的JavaScript SDK ,如以下内容:

      • angular-easyfb
      • angular-facebook

      我不感兴趣使用它们,现在,因为我想学习如何编写一个角度的服务喽。 所以,请保持限于帮我解决了问题答案的我的code 的,而不是暗示我用别人的

      I'm not interested in using them right now, because I wanted to learn how to write an Angular service myself. So please keep answers restricted to helping me fix the problems in my code, instead of suggesting that I use someone else's.

      所以,他这样说,有谁知道为什么我的测试是不工作?

      推荐答案

      TL; DR

      呼叫 $ rootScope $摘要()从测试code,它会传递:

      Call $rootScope.$digest() from your test code and it'll pass:

      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()等待()调用,因为正在这里不需要他们(无正在执行实际的异步调用)。

      Note: I removed run() and wait() calls because they're not needed here (no actual async calls being performed).

      龙解释

      下面是发生了什么:当你调用 getUserLoginStatus(),它在内部运行 FB.getLoginStatus()这反过来执行其回调的立即,因为它应该,因为你已经嘲笑它做precisely这一点。但是,你的 $范围。$适用()呼叫是指回调中,所以它被执行的的的。然后( )语句在测试中。而且,由于则()创建一个新的希望,新的消化需要这一承诺得到解决。

      Here's what's happening: When you call getUserLoginStatus(), it internally runs FB.getLoginStatus() which in turn executes its callback immediately, as it should, since you've mocked it to do precisely that. But your $scope.$apply() call is within that callback, so it gets executed before the .then() statement in the test. And since then() creates a new promise, a new digest is required for that promise to get resolved.

      相信这个问题不会在浏览器中发生,因为的二分之一的原因:

      I believe this problem doesn't happen in the browser because of one out of two reasons:


      1. FB.getLoginStatus()不立即调用它的回调,所以任何则()通话先运行;或

      2. 别的东西在应用程序中触发一个新的周期消化

      1. FB.getLoginStatus() doesn't invoke its callback immediately so any then() calls run first; or
      2. Something else in the application triggers a new digest cycle.

      所以,把它包起来,如果你创建一个测试中的承诺,明确与否,你将不得不触发在某些时候摘要周期为了这个承诺得到解决。

      So, to wrap it up, if you create a promise within a test, explicitly or not, you'll have to trigger a digest cycle at some point in order for that promise to get resolved.

      这篇关于AngularJS无极回调JasmineJS试验未触发时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆