问题介绍
我正在尝试对一个包装了Facebook JavaScript SDK FB
对象的AngularJS服务进行单元测试,但是测试不起作用,我也无法弄清原因。同时,在使用Karma测试运行器运行JasmineJS单元测试时,服务代码在浏览器中运行时是有效的。
$q
对象测试异步方法。我已经设置了测试以使用Jasmine 1.3.1异步测试方法运行,但是waitsFor()
函数从不返回true
(请参见下面的测试代码),它只会在5秒后超时。(Karma还没有使用Jasmine 2.0异步测试API)。我认为这可能是因为承诺的
then()
方法从未被触发(我已经设置了console.log()
来显示),即使我在异步方法返回时调用$scope.$apply()
,让Angular知道它应该运行一个digest循环并触发then()
回调……但我可能错了。这是运行测试时产生的错误输出:
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)
代码
这是我对该服务进行的单元测试(请查看内联注释以了解我目前发现的内容):
'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
这是我正在测试的Angular服务(请查看内联注释,了解我目前发现了什么):
'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
};
}]);
资源
这是我已经查看过的其他资源列表,以尝试解决这个问题。
-
这里解释了如何在 Angular 中使用 promises,并且给出了一个使用 promises 的代码单元测试的例子(注意解释为什么需要调用
$scope.$apply()
来触发then()
回调)。 Jasmine 异步测试示例
- Jasmine.Async: 让 Jasmine 异步测试变得更加容易
使用 Jasmine 2.0.0 测试异步 JavaScript
这些示例展示了如何使用 Jasmine 1.3.1 异步方法来测试实现 Promise 模式的对象。它们与我自己测试中使用的模式略有不同,后者是直接来自 Jasmine 1.3.1 异步测试文档 的示例。
StackOverflow 答案
- Angular JS 中的 Promise 回调未被调用
- angularjs - 控制器中 promise 从未被解决
- AngularJS promise 在从服务返回时不触发
摘要
请注意,我知道已经有其他针对Facebook JavaScript SDK的Angular库了,例如以下:
我现在不想使用它们,因为我想学习如何自己编写一个Angular服务。 因此,请限制答案以帮助我修复我的代码中的问题,而不是建议我使用别人的代码。
那么,说了这么多,有人知道为什么我的测试没通过吗?
$rootScope
传递给了我的服务,因此服务基本上调用了$rootScope.$apply()
。所以,是的,我尝试过了。但是谢谢您的建议! - user456814