AngularJS jasmine promise 测试因超时而失败
AngularJS jasmine promise testing fails because of timeout
我正在尝试测试我的登录控制器,它看起来像这样:
describe('LoginController', function() {
beforeEach(module('task6'));
var $controller, LoginService;
beforeEach(inject(function(_$controller_, _LoginService_) {
$controller = _$controller_;
LoginService = _LoginService_;
}));
describe('LoginController.submitLogin', function() {
it('tests if such user exists', function(done) {
var $scope = {};
var controller = $controller('LoginController', {$scope: $scope});
var resultValue;
controller.loginField = 'John';
controller.password = 'Smith';
LoginService.signIn(controller.loginField,
controller.password)
.then(function() {
expect(true).toBe(true);
done();
});
});
});
});
其中登录函数是:
function signIn(loginField, password) {
var defer = $q.defer();
if (loginField && password) {
defer.resolve("nice one");
} else {
defer.reject("oh dear");
}
return defer.promise;
}
但总是失败 "Async callback was not invoken within timeout specified..."
您需要为控制器创建 scope
对象作为 $rootScope
的实例:
describe('LoginController', function() {
beforeEach(module('task6'));
var $controller, LoginService;
// Inject the $rootScope service for use in creating new scope objects.
beforeEach(inject(function($rootScope, _$controller_, _LoginService_) {
$controller = _$controller_;
LoginService = _LoginService_;
}));
describe('LoginController.submitLogin', function() {
it('tests if such user exists', function(done) {
// Create a new scope for the controller.
var scope = $rootScope.$new();
var controller = $controller('LoginController', {$scope: scope});
var resultValue;
controller.loginField = 'John';
controller.password = 'Smith';
LoginService.signIn(controller.loginField,
controller.password)
.then(function() {
expect(true).toBe(true);
done();
});
});
});
});
您需要在测试结束时调用 $scope.$digest()
一次。承诺永远不会在创建它的同一摘要周期内解决。
说明
虽然框架可能允许这样做,但开发人员选择不允许。请参阅此示例,但它可能有问题:
function login(login, password) {
LoginService.signIn(login, password)
.then(function() {
cancelLoadingAnimation();
});
startLoadingAnimation();
}
通常 promise 是异步解决的,所以我们在这里没有问题。加载动画在登录函数中启动,并在登录成功时取消。但是想象一下这个承诺立即得到解决(例如在像你这样的测试中)!现在可以在动画开始之前取消动画。
当然,这可以通过将 startLoadingAnimation()
的调用移到 signIn()
的调用之上来解决。但是,当 promise 始终异步解决时,更容易推理您的代码。
更新: 正如@gnerkus 在他的回答中所述,您必须创建 $scope
作为 $rootScope
的子项。但是,仅此并不能解决问题。您将必须同时执行这两项操作。
我猜,@gnerkus 和@lex82 都是对的 - 我需要 运行 $digest 循环来获取承诺,但我仍然需要一个真实范围的引用来执行此操作。这是我的代码的最终工作版本:
describe('LoginController', function() {
beforeEach(module('task6'));
var $rootScope, $controller, LoginService;
beforeEach(inject(function(_$rootScope_, _$controller_, _LoginService_) {
$rootScope = _$rootScope_;
$controller = _$controller_;
LoginService = _LoginService_;
}));
describe('LoginController.submitLogin', function() {
it('tests if such user exists', function(done) {
var $scope = $rootScope.$new();
var controller = $controller('LoginController',
{$scope: $scope});
controller.loginField = 'John';
controller.password = 'Smith';
LoginService.signIn(controller.loginField,
controller.password)
.then(function(logged) {
expect(true).toBe(true);
done();
})
$scope.$digest();
});
});
});
谢谢大家!
我正在尝试测试我的登录控制器,它看起来像这样:
describe('LoginController', function() {
beforeEach(module('task6'));
var $controller, LoginService;
beforeEach(inject(function(_$controller_, _LoginService_) {
$controller = _$controller_;
LoginService = _LoginService_;
}));
describe('LoginController.submitLogin', function() {
it('tests if such user exists', function(done) {
var $scope = {};
var controller = $controller('LoginController', {$scope: $scope});
var resultValue;
controller.loginField = 'John';
controller.password = 'Smith';
LoginService.signIn(controller.loginField,
controller.password)
.then(function() {
expect(true).toBe(true);
done();
});
});
});
});
其中登录函数是:
function signIn(loginField, password) {
var defer = $q.defer();
if (loginField && password) {
defer.resolve("nice one");
} else {
defer.reject("oh dear");
}
return defer.promise;
}
但总是失败 "Async callback was not invoken within timeout specified..."
您需要为控制器创建 scope
对象作为 $rootScope
的实例:
describe('LoginController', function() {
beforeEach(module('task6'));
var $controller, LoginService;
// Inject the $rootScope service for use in creating new scope objects.
beforeEach(inject(function($rootScope, _$controller_, _LoginService_) {
$controller = _$controller_;
LoginService = _LoginService_;
}));
describe('LoginController.submitLogin', function() {
it('tests if such user exists', function(done) {
// Create a new scope for the controller.
var scope = $rootScope.$new();
var controller = $controller('LoginController', {$scope: scope});
var resultValue;
controller.loginField = 'John';
controller.password = 'Smith';
LoginService.signIn(controller.loginField,
controller.password)
.then(function() {
expect(true).toBe(true);
done();
});
});
});
});
您需要在测试结束时调用 $scope.$digest()
一次。承诺永远不会在创建它的同一摘要周期内解决。
说明
虽然框架可能允许这样做,但开发人员选择不允许。请参阅此示例,但它可能有问题:
function login(login, password) {
LoginService.signIn(login, password)
.then(function() {
cancelLoadingAnimation();
});
startLoadingAnimation();
}
通常 promise 是异步解决的,所以我们在这里没有问题。加载动画在登录函数中启动,并在登录成功时取消。但是想象一下这个承诺立即得到解决(例如在像你这样的测试中)!现在可以在动画开始之前取消动画。
当然,这可以通过将 startLoadingAnimation()
的调用移到 signIn()
的调用之上来解决。但是,当 promise 始终异步解决时,更容易推理您的代码。
更新: 正如@gnerkus 在他的回答中所述,您必须创建 $scope
作为 $rootScope
的子项。但是,仅此并不能解决问题。您将必须同时执行这两项操作。
我猜,@gnerkus 和@lex82 都是对的 - 我需要 运行 $digest 循环来获取承诺,但我仍然需要一个真实范围的引用来执行此操作。这是我的代码的最终工作版本:
describe('LoginController', function() {
beforeEach(module('task6'));
var $rootScope, $controller, LoginService;
beforeEach(inject(function(_$rootScope_, _$controller_, _LoginService_) {
$rootScope = _$rootScope_;
$controller = _$controller_;
LoginService = _LoginService_;
}));
describe('LoginController.submitLogin', function() {
it('tests if such user exists', function(done) {
var $scope = $rootScope.$new();
var controller = $controller('LoginController',
{$scope: $scope});
controller.loginField = 'John';
controller.password = 'Smith';
LoginService.signIn(controller.loginField,
controller.password)
.then(function(logged) {
expect(true).toBe(true);
done();
})
$scope.$digest();
});
});
});
谢谢大家!