由于超时,AngularJS jasmine promise测试失败

时间:2016-01-18 18:53:07

标签: javascript angularjs jasmine promise q

我试图测试我的登录控制器,如下所示:

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

但是它始终失败并且#34;异步回调未在指定的超时时间内被调用..."

3 个答案:

答案 0 :(得分:2)

您需要为控制器创建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();
        }); 
    });
  });
});

答案 1 :(得分:2)

您需要在测试结束时拨打$scope.$digest()一次。承诺永远不会在它创建的同一摘要周期内得到解决。

<强>解释

虽然框架允许这样做,但开发人员选择不这样做。看到这个例子虽然可能有问题:

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

    startLoadingAnimation(); 
}

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

当然,可以通过将startLoadingAnimation()的号码转移到signIn()以上的呼叫来解决此问题。但是,当promises总是异步解析时,更容易推理你的代码。

更新:正如@gnerkus在回答中所述,您必须创建$scope作为$rootScope的孩子。但是,仅凭这一点并不能解决问题。你必须要做到这两点。

答案 2 :(得分:1)

我想,@ gnerkus和@ lex82都是正确的 - 我需要为promises运行$ 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();
        });
    });
}); 

谢谢你们,伙计们!