好吧,所以我长期以来一直在绊倒一些问题,我想听听社区其他人的意见。
首先,让我们看看一些抽象的控制器。
function Ctrl($scope, anyService) {
$scope.field = "field";
$scope.whenClicked = function() {
util();
};
function util() {
anyService.doSmth();
}
}
显然我们在这里:
$scope
并注入了一些服务的控制器的常规脚手架util()
现在,我想在单元测试(Jasmine)中介绍这个类。但是,问题是我想验证当我点击(调用whenClicked()
)某个项目时将调用util()
方法。我不知道该怎么做,因为在Jasmine测试中我总是得到错误,要么util()
的模拟没有被定义或者没被调用。
注意:我并没有尝试修复这个特定的例子,我一般都在询问测试这样的代码模式。所以请不要告诉我“究竟是什么错误”。我问的是如何做到这一点,而不是如何解决这个问题。
我一直在尝试各种方法:
$scope
因为我没有将此函数附加到此对象(它通常以消息Expected spy but got undefined
或类似的结尾)Ctrl.util = util;
将这些函数附加到控制器对象,然后验证Ctrl.util = jasmine.createSpy()
之类的模拟,但在这种情况下Ctrl.util
未被调用,因此测试失败util()
更改为附加到this
对象并再次嘲笑Ctrl.util
,但没有运气好吧,我无法找到解决这个问题的方法,我期待JS忍者的一些帮助,一个工作小提琴将是完美的。
答案 0 :(得分:42)
您提供的控制器功能将由Angular用作构造函数;在某些时候,将使用new
调用它来创建实际的控制器实例。如果您确实需要在控制器对象中具有未暴露于$ scope但可用于间谍/存根/模拟的函数,则可以将它们附加到this
。
function Ctrl($scope, anyService) {
$scope.field = "field";
$scope.whenClicked = function() {
util();
};
this.util = function() {
anyService.doSmth();
}
}
现在调用var ctrl = new Ctrl(...)
或使用Angular $controller
服务检索Ctrl
实例时,返回的对象将包含util
函数。
您可以在此处查看此方法:http://jsfiddle.net/yianisn/8P9Mv/
答案 1 :(得分:32)
将范围限制在范围内的是污染。你想要做的是将该逻辑提取到一个单独的函数中,然后将其注入到Controller中。即。
function Ctrl($scope, util) {
$scope.field = "field";
$scope.whenClicked = function() {
util();
};
}
angular.module("foo", [])
.service("anyService", function(...){...})
.factory("util", function(anyService) {
return function() {
anyService.doSmth();
};
});
现在您可以使用Ctrl 以及“util”模拟单元测试。
答案 2 :(得分:7)
我打算采用不同的方法。你不应该测试私有方法。这就是为什么它们是私有的 - 这是一个与使用无关的实现细节。
例如,如果您意识到util已在多个地方使用但现在,基于其他代码重构,它只在这一个地方调用。为什么有一个额外的函数调用?只需在anyService.doSmith()
中加入$scope.whenClicked()
。根据上述建议,假设您正在测试util()
被调用,即使您没有更改程序的功能,您的测试也会中断。单元测试的一个主要价值是简化重构而不会破坏事物,所以如果你没有破坏,测试不应该失败。
您需要做的是确保在调用$scope.whenClicked
时,还会调用anyService.doSmth()
。你只需要:
spyOn(anyService,'doSmith')
scope.whenClicked();
expect(anyService.doSmith).toHaveBeenCalled();
答案 3 :(得分:2)
我正在添加一个包含我当前方法的答案,希望得到一些评论,或许可以激发讨论这是否是一个很好的解决方案。
我们将私有函数附加到控制器函数(从而使它们公开,从而实现模拟)。为了避免必须一直重复控制器名称并使语法更具吸引力,我们创建了self
对象,该对象保存对控制器函数的引用。所以它变成了:
function Ctrl($scope, anyService) {
$scope.field = "field";
$scope.whenClicked = function() {
self.util();
};
var self = Ctrl; // For the sake of syntax simplicity only
self.util = function() {
anyService.doSmth();
};
}
然后在单元测试中我们现在可以使用:
Ctrl.util = jasmine.createSpy("util()");
expect(Ctrl.util).toHaveBeenCalled();
我仍然不喜欢这个,但我认为这是最简单的方法。我希望有人会找到更好的方法。