如何在AngularJS中测试.run?

时间:2014-05-14 18:53:04

标签: angularjs karma-runner

myApp.run([
  '$rootScope', 'userService', function($rootScope, userService) {
    return userService.isAuthenticated().then(function(response) {
      if (response.data.authenticated) {
        return $rootScope.$broadcast('login', response.data);
      } else {
        return userService.logout();
      }
    });
  }
]);

这是我在init.js文件中的代码。我该如何对此进行单元测试?

5 个答案:

答案 0 :(得分:6)

run(..)块进行单元测试就像在茉莉花中加载module(..)一样简单。

以下所有代码均可在this plunker中找到。

考虑以下非常简单的 app.js

var _idx = 1;
window.log = function(s) {
  console.log('(' + _idx++ + ') ' + s);
};

var app = angular.module('myapp', []);

app.run(function($rootScope) {
  log('run block');
  $rootScope.valueSetInRun = 666;

});

app.controller('MainCtrl', function($scope) {
  log('MainCtrl block');
});

请注意,此应用中的标记无关紧要。

现在考虑以下测试:

describe('myapp', function() {

  beforeEach(module('myapp'));

  beforeEach(inject(function($rootScope) {
    log('beforeEach');
  }));

  it('should allow me to test the run() block', inject(function ($rootScope) {
    log('it block');
    expect( $rootScope.valueSetInRun ).toBe(666);
  }));
});

这是控制台输出:

(1) run block 
(2) MainCtrl block 
(3) run block 
(4) beforeEach 
(5) it block 

请注意,测试通过了

一些重要的观察

  • 在正常的应用执行期间记录前两个控制台输出(1)(2)
  • 在测试运行时输出日志消息(3)(5)

鉴于上述情况,我们可以得出以下结论:

  1. 在单元测试中,模块'myapp'已实例化
  2. 在单元测试中,MainCtrl未实例化
  3. 在单元测试中,执行了run
  4. 运行单元测试时,首先执行beforeEach...run块,然后执行beforeEach,最后执行it块。
  5. 第二个 beforeEach 对单元测试run块非常有用(请注意,您可以放置beforeEach之前的beforeEach(module('myapp'));块,但是如果您尝试在该块中创建注入器,则测试将失败)
  6. 最后,我们可以得出结论,上面的代码就是如何测试run块。对于您的特定测试,凭借这些知识,可以构建使用dependency injection mockingJasmine's toHaveBeenCalled() assertion进行单元测试。

答案 1 :(得分:1)

一种简单的方法是在$ rootScope中存储'身份验证'对象。在单元测试中,期望该值为空或未定义,然后填充服务中的对象,并根据该对象的值或长度重新测试。

答案 2 :(得分:1)

我还没有测试过这个,但我很确定它是这样运作的。 app.run方法意味着在给定模块加载了所有依赖项之后,但在初始化任何控制器之前运行。在您的测试中(假设您正在使用茉莉花),您可以通过执行以下操作来调用该初始化程序:

describe('MyApp', function(){
    beforeEach(function(){
        module('MyApp');
    });
    ...
    // more test code goes here
    ...
});

module('MyApp')的调用是初始化模块的内容,"应该" (理论上,因为我还没有实际测试过它)之后立即调用run方法。这意味着你应该能够通过做一些稍微不同的事情来测试运行块:

describe('MyApp', function(){
    beforeEach(function(){
        // whatever you need
    });

    describe('.run()', function(){
        it('should check if user is authenticated', function(){
            // expect not to have checked
            module('MyApp'); // this should call the .run function
            // expect to have checked
        });
    });
});

我可能完全离开,但我相信这应该让你开始。希望它有所帮助。

答案 3 :(得分:1)

我有类似的问题,并决定将所有内容都提取到应用程序级服务中。对我而言,它感觉有点混乱,组织得很糟糕 - 特别是如果一切都整洁而且有条不紊的潮流。跑道感觉有点像倾倒地。

因此我们可以访问D.I.与配置块不同,我们可以将所有代码都放在服务中并通过init方法激活它。

申请服务

myApp.factory('Application', function($rootScope, userService) {

    function setAuthenticated(response) {
        if (response.data.authenticated) {
          return $rootScope.$broadcast('login', response.data);
        } else {
          return userService.logout();
        }
    }

    function authenticate() {
      userService.isAuthenticated()
        .then(setAuthenticated);
    }

    function init() {
      authenticate();
    }

    return {
      init: init
    }
  }
]);

运行方法

myApp.run(function(Application) {
  Application.init();
});

我们的应用程序服务现在可以负责应用程序启动时需要完成的所有工作 - 它只是一个简单的工厂,因此我们可以对它进行单元测试 - 我们可以根据需要添加它

作为旁注,我更愿意让ngmin处理我的角度缩小问题 - 为开发人员带来更清晰,更易读的代码。

答案 4 :(得分:1)

你可以尝试嘲笑你的$rootScopeuserService

  var fakeRootScope;
  var fakePromise;
  var fakeUserService;

  beforeEach(function() {
      fakeRootScope = {}; //a fake $rootScope so that we can mock the $broadcast function

     //a fake promise with a .then function which calls the provided callback
     //  passing in a fake response provided by our test cases.
      fakePromise = {
         response: null,
         then: function(callback) {
             callback(this.response);
         }
      };

      //mock the userService by $provide.factory assuming that you register 
      //your userService using .factory. If you use .service to register, 
      // you might need to use .service to create the mock
      //mock the $rootScope by $provide.value
      module('plunker', function($provide) {
         $provide.factory("userService", function() {

          fakeUserService = {
             isAuthenticated: function() {
                    return fakePromise;
             },
                  logout: function() {}
           };

           fakeUserService.logout = jasmine.createSpy("logout");

           return fakeUserService;
        });

       fakeRootScope.$broadcast = jasmine.createSpy("$broadcast");
       $provide.value('$rootScope', fakeRootScope);
     });
  });

在您的测试中,您可以提供假authenticated来测试2个案例:

    it('Is Not authenticated', function() {
      fakePromise.response = {
        data: {
          authenticated: false //test login failing case
        }
      }

      inject(function(userService) {
         //verify that userSerivce.logout is called when login failed
          expect(userService.logout).toHaveBeenCalled();
      });
    });

    it('Is authenticated', function() {
      fakePromise.response = {
        data: {
          authenticated: true //test login succeeding case
        }
      }

      inject(function($rootScope) {
 //verify that $rootScope.$broadcast is called with 2 parameters when login succeed 

 expect($rootScope.$broadcast).toHaveBeenCalledWith('login',fakePromise.response.data);
      });
    });

DEMO