Javascript AngularJS jasmine promise测试因超时而失败

Javascript AngularJS jasmine promise测试因超时而失败,javascript,angularjs,jasmine,promise,q,Javascript,Angularjs,Jasmine,Promise,Q,我正在尝试测试我的登录控制器,如下所示: describe('LoginController', function() { beforeEach(module('task6')); var $controller, LoginService; beforeEach(inject(function(_$controller_, _LoginService_) { $controller = _$controller_; LoginServ

我正在尝试测试我的登录控制器,如下所示:

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();
            }); 
        });
    });
});
其中,signIn函数为:

function signIn(loginField, password) {
        var defer = $q.defer();
   if (loginField && password) {
        defer.resolve("nice one");
   } else {
        defer.reject("oh dear");
   }
   return defer.promise;
}
但如果在指定的超时时间内未调用异步回调,则始终会失败…

您需要为控制器创建scope对象作为$rootScope的实例:

您需要为控制器创建scope对象作为$rootScope的实例:

您需要在测试结束时调用$scope.$digest一次。承诺永远不会在其创建的同一个摘要周期内得到解决

解释

虽然框架可能允许这样做,但开发人员选择不这样做。请参见此示例,但它可能存在问题:

function login(login, password) {
    LoginService.signIn(login, password)
        .then(function() {
            cancelLoadingAnimation();
        });

    startLoadingAnimation(); 
}
通常承诺是异步解决的,所以这里没有问题。加载动画在登录功能中启动,并在登录成功时取消。但是想象一下,这个承诺在一个像你这样的测试中立即得到了解决!现在,动画可能在开始之前就被取消了

当然,这可以通过将startLoadingAnimation调用移到signIn调用之上来解决。然而,当承诺总是异步解析时,对代码进行推理就容易多了

更新:正如@gnerkus在他的回答中所说的,您必须创建$scope作为$rootScope的子级。然而,单靠这一点并不能解决问题。您必须同时执行这两项操作。

您需要在测试结束时调用$scope.$digest一次。承诺永远不会在其创建的同一个摘要周期内得到解决

解释

虽然框架可能允许这样做,但开发人员选择不这样做。请参见此示例,但它可能存在问题:

function login(login, password) {
    LoginService.signIn(login, password)
        .then(function() {
            cancelLoadingAnimation();
        });

    startLoadingAnimation(); 
}
通常承诺是异步解决的,所以这里没有问题。加载动画在登录功能中启动,并在登录成功时取消。但是想象一下,这个承诺在一个像你这样的测试中立即得到了解决!现在,动画可能在开始之前就被取消了

当然,这可以通过将startLoadingAnimation调用移到signIn调用之上来解决。然而,当承诺总是异步解析时,对代码进行推理就容易多了


更新:正如@gnerkus在他的回答中所说的,您必须创建$scope作为$rootScope的子级。然而,单靠这一点并不能解决问题。您必须同时执行这两项操作。

我想,@gnerkus和@lex82都是正确的-我需要运行$digest cycle以获得承诺,但我仍然需要一个对实际范围的引用来执行此操作。以下是我的代码的最终工作版本:

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();
        });
    });
}); 

谢谢大家

我想,@gnerkus和@lex82都是对的-我需要运行$digest cycle来获得承诺,但我仍然需要一个对真实范围的引用来完成这项工作。以下是我的代码的最终工作版本:

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();
        });
    });
}); 
谢谢大家