【问题标题】:Angular Controller Testing: check dependency injection is done rightAngular 控制器测试:检查依赖注入是否正确
【发布时间】:2017-06-02 12:49:48
【问题描述】:

有时,通常在重构控制器(或工厂)时,我最终通过了控制器测试,但是当我的应用程序启动并运行时,由于我忘记添加/更新依赖注入,它在某些时候总是崩溃。

我的意思是,假设我有以下控制器,我曾经有一个oldDependency,但由于重构,我改用newDependency。我用新的更改更新了MyCtrl.$inject,但我忘记更新传递给MyCtrl 函数的依赖项:

angular
    .module('my-module')
    .controller('MyCtrl', MyCtrl);

MyCtrl.$inject = [
    'firstDependency',
    'secondDependency',
    'newDependency' // this has been only updated here
];

function MyCtrl(firstDependency, secondDependency, oldDependency) {
    var vm = this;

    // My Controller's code
    // etc...

    function someFunc(x) {
       // here I use newDependency
       return newDependency.doSomething(x);
    }
}   

那么接下来会发生什么?我去更新MyCtrl测试,其实我记得更新传递给$controller()的依赖对象:

// MyCtrl testing code
var MyCtrl = $controller('VolunteerResignCtrl', {
    firstDependency: firstDependency,
    secondDependency: secondDependency,
    newDependency: newDependency
});

因此,所有MyCtrl 测试都通过了,所以我认为没有任何问题。但它确实做到了。

谁能告诉我这是否可以通过某种方式进行测试并避免我的应用在未来因这个原因而失败?

【问题讨论】:

  • 但这应该可以在您的应用程序中使用,您注入的字符串不必与函数中的变量名称匹配,否则,像缩小之类的东西将不起作用。
  • 你是对的!但大多数时候我可能还会更新控制器的代码,我使用newDependency。对这些案例有什么想法吗?
  • @George 我刚刚更新了它!
  • 是的,就是这样!我了解正在发生的事情,这就是为什么我想知道是否有任何方法可以避免这种行为。谢谢@乔治! ;-)
  • 我想我可能知道为什么。在您的测试用例中,您如何定义 newDependency 函数,因为您可能会将其暴露在全局范围内。尝试将您的测试包装在 IIFE 中。正如您所看到的,this example更高 范围内定义了函数 newDependency,它能够解决它。

标签: javascript angularjs testing frontend


【解决方案1】:

这似乎是 JS 作用域而不是 Angulars 依赖注入的问题。尝试将您的测试用例和您用于它们的函数包装在 IIFE 中,因为它们不是,JS 将扩大范围,直到它可以找到一个名为 newDependency 的变量,这可能是您的测试正在运行的原因,但是它在您的应用程序中崩溃。

从下面的例子可以看出

没有 IIFE

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

myApp.factory('firstDependency', function() {
  return {}
}());
myApp.factory('secondDependency', function() {
  return {}
}());
myApp.factory('newDependency', function() {
  return {}
}())

myApp.controller('MyCtrl', MyCtrl);
myApp.controller('TestCtrl', TestCtrl);

MyCtrl.$inject = [
  'firstDependency',
  'secondDependency',
  'newDependency'
];

function MyCtrl(firstDependency, secondDependency, oldDependency) {
  var vm = this;
  vm.someFunc = function(x) {
    // Will work fine as newDependency is defined in a higher scope :c
    return newDependency.doSomething(x);
  }
}

var firstDependency = function() {
  return {}
}();
var secondDependency = function() {
  return {}
}();
var newDependency = function() {
  return {
    doSomething: function(x) {
      return x;
    }
  }
}()

function TestCtrl($scope, $controller) {
  var test = $controller('MyCtrl', {
    firstDependency: firstDependency,
    secondDependency: secondDependency,
    newDependency: newDependency
  });

  $scope.someFunc = test.someFunc("you're inside the new dependency");
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="TestCtrl">
    {{someFunc}}!
  </div>
</div>

IIFE

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

myApp.factory('firstDependency', function() {
  return {}
}());
myApp.factory('secondDependency', function() {
  return {}
}());
myApp.factory('newDependency', function() {
  return {}
}())

myApp.controller('MyCtrl', MyCtrl);


MyCtrl.$inject = [
  'firstDependency',
  'secondDependency',
  'newDependency'
];

function MyCtrl(firstDependency, secondDependency, oldDependency) {
  var vm = this;
  vm.someFunc = function(x) {
    //Will throw an error, but this time it's a good error because that's what you want!
    return newDependency.doSomething(x);
  }
}

(function(myApp) {


  var firstDependency = function() {
    return {}
  }();
  var secondDependency = function() {
    return {}
  }();
  var newDependency = function() {
    return {
      doSomething: function(x) {
        return x;
      }
    }
  }()

  myApp.controller('TestCtrl', TestCtrl);

  function TestCtrl($scope, $controller) {
    var test = $controller('MyCtrl', {
      firstDependency: firstDependency,
      secondDependency: secondDependency,
      newDependency: newDependency
    });

    $scope.someFunc = test.someFunc("you're inside the new dependency");
  }

})(myApp)
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
  <div ng-controller="TestCtrl">
    {{someFunc}}!
  </div>
</div>

【讨论】:

    【解决方案2】:

    从 js 的角度来看,变量的名称并不重要。 以下功能相同:

    function f(SomeService) {
      SomeService.test();
    }
    
    function f(NewService) {
      NewService.test();
    }
    
    function f(a) {
      a.test();
    }
    

    这就是你需要 $inject 的原因——因为你传递了他们的字符串。 在 $inject 中更改名称后 - 您可以在函数中更改它,但也可能不会更改它。一点都不重要。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多