使用Angular进行控制器测试的单元测试状态

时间:2015-07-15 01:17:16

标签: angularjs unit-testing jasmine

我有一个使用$stateParams的基本控制器。

angular.module('example')
  .controller('SampleCtrl', ['$stateParams',
      function($stateParams) {
         var vm = this;
         vm.isSomething = ($stateParams.isSomething === 'true') ? true : false;
      }
  ]);

在我的单元测试中,我需要在一次测试中将$stateParams.isSomething设置为true,在另一次测试中设置为false。

 describe('SampleCtrl Test', function() {

    var ctrl, scope, $stateParams;

    // set default stateParams
    $stateParams = { isSomething: 'false' };

    beforeEach(function(){
       inject(function($rootScope, $controller){
          scope = $rootScope.$new();

          ctrl = $controller('SampleCtrl', {
            $scope: scope,
            $stateParams: $stateParams
          });
       });
    });

    describe('when isSomething is false', function() {
         it('should be false', function() {
            expect(ctrl.isSomething).toBe(false);
         });
    });

    describe('when isSomething is true', function() {

         beforeEach(function() {
           $stateParams.isSomething = 'true';
         });

         it('should be true', function() {
            // THIS IS FAILING
            expect(ctrl.isSomething).toBe(true);
         });
    });

 });

如何针对不同的测试正确模拟$stateParams的不同状态?

2 个答案:

答案 0 :(得分:6)

我认为您需要使用更新的范围对象再次实例化控制器。

您也有一些命名问题,请参阅下面代码中的注释。

DEMO

describe('SampleCtrl Test', function() {

    var ctrl, scope, $stateParams, $controller;

    // set default stateParams

    // you have called it 'something' in your controller not 'isSomething'
    $stateParams = { something: 'false' };

    beforeEach(function(){

      // load module
      module('example');


       inject(function($rootScope, _$controller_){
          scope = $rootScope.$new();

          // angular removes the _'s for you so you can call it $controller
          $controller = _$controller_;

          ctrl = $controller('SampleCtrl', {
            $scope: scope,
            $stateParams: $stateParams
          });
       });
    });

    describe('when isSomething is false', function() {
         it('should be false', function() {
            expect(ctrl.isSomething).toBe(false);
         });
    });

    describe('when isSomething is true', function() {

         beforeEach(function() {

           // you have called it 'something' in your controller not 'isSomething'
           $stateParams.something = 'true';

           // instantiate a new controller with the updated $stateParams object
           ctrl = $controller('SampleCtrl', {
              $scope: scope,
              $stateParams: $stateParams
            });

         });

         it('should be true', function() {
            // THIS IS FAILING
            expect(ctrl.isSomething).toBe(true);
         });
    });

 });

答案 1 :(得分:1)

你遇到的问题是describe的beforeEach('当isSomething为true')在之前的describe('SampleCtrl Test')之后评估,即控制器已经实例化当$ stateParams值改变时。正如Matt在他的回答中写道,你需要在改变发生后实例化控制器。

我通常会使用简单的解决方法。由于beforeEach在控制器之后被实例化,我通常会写一个空的it语句,其中错误的stateParams被发送到控制器并忽略那个。对于所有前面的语句,将发生对stateParams更改的更改,然后控制器将使用正确的变量:

describe('when isSomething is true', function() {
    beforeEach(function() {
         $stateParams.isSomething = 'true';
    });

    it('should take one iteration to set the state parameter', function() {
        // do nothing as the controller will not use the new $stateParams yet
    });

    it('should be true', function() {
        // THIS IS NO LONGER FAILING
        expect(ctrl.isSomething).toBe(true);
    });
});

虽然不是最美丽的解决方案,但它似乎是最简单的