代码之家  ›  专栏  ›  技术社区  ›  Vladimir Mironov

AngularJS jasmine promise测试因超时而失败

  •  1
  • Vladimir Mironov  · 技术社区  · 9 年前

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

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

    但它总是失败,“在指定的超时内未调用异步回调…”

    3 回复  |  直到 9 年前
        1
  •  2
  •   lex82    9 年前

    你需要打电话 $scope.$digest() 考试结束时一次。承诺永远不会在其创建的同一个摘要周期内得到解决。

    解释

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

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

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

    当然,这可以通过调用 startLoadingAnimation() 高于呼叫 signIn() 然而,当promise总是异步解析时,对代码进行推理要容易得多。

    更新: 正如@gnerkus在回答中所说,你必须创建 $scope 作为 $rootScope 然而,仅凭这一点并不能解决问题。你必须同时做这两件事。

        2
  •  2
  •   gnerkus    9 年前

    您需要创建 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();
            }); 
        });
      });
    });
    
        3
  •  1
  •   Vladimir Mironov    9 年前

    我想,@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();
            });
        });
    }); 
    

    谢谢你们,伙计们!