在 Angular 单元测试中测试控制器初始化进程
Testing controller init process in Angular unit test
是否可以测试Angular控制器的init进程?
代码如下:
var app = angular.module('led', []);
app.controller('zeppelin', ['$scope', function ($scope) {
$scope.love = function () {
return 2;
};
$scope.tangerine = $scope.love();
$scope.prove = function () {
$scope.love();
};
$scope.prove();
$scope.love();
}]);
测试代码:
scope = _$rootScope_.$new();
controller = _$controller_('zeppelin', {
$scope: scope
});
proved = sinon.spy();
loved = sinon.stub().returns(7);
scope.prove = proved;
scope.love = loved;
expect(proved.callCount).toBe(1); //=> 0
expect(loved.callCount).toBe(2); //=> 0
scope.prove();
expect(proved.callCount).toBe(2); //=> 1
expect(loved.callCount).toBe(3); //=> 0
我对上述代码的问题是:
- 我知道预期会失败,因为控制器已经初始化。如果我在初始化之前设置 scope.prove = spied ,那么即使在显式调用它们之后,我也会收到 0 。是否有测试 init 进程的有效方法?
- 如何存根 scope.love,所以 scope.tangerine 从存根接收数据,而不是从存根接收数据控制器?
- 为什么调用scope.prove()后,loved.callCount不自增?
对我来说最奇怪的案例是 3,因为我想不出任何合理的解释来解释这种奇怪的行为。
1.
控制器只是一个构造函数,它接受 $scope
对象作为参数。您正在范围
上定义函数
$scope.love = function () {
return 2;
};
$scope.prove = function () {
$scope.love();
};
然后立即调用它们
$scope.prove();
$scope.love();
无法拦截此进程(在 Angular 中或一般情况下)以在控制器末尾首次调用函数之前监视这些函数。如果你在初始化控制器之前设置$scope.prove = spied
,那么它将被控制器简单地覆盖,如果你设置它之后然后调用函数时间谍还不存在。
2.
由于上述原因,您无法及时将 $scope.love
的 return 值分配给 $scope.tangerine
。如果能够在测试中修改控制器初始化行为对您来说真的很重要,您应该使 love
和可注入服务将其注入控制器,并在您的测试中模拟它。
3.
$scope.prove
被您的间谍覆盖 (scope.prove = proved
)。您正在丢失在您的控制器中创建的调用 $scope.love
的原始函数。相反,您需要监视现有功能。例如,
var controller = $controller('zeppelin', { $scope: scope });
// here, the controller has initialized scope.prove and scope.love
var loved = sinon.spy(scope, 'love');
var proved = sinon.spy(scope, 'prove');
scope.prove();
// both callCounts are incremented
expect(proved.callCount).toBe(1);
expect(loved.callCount).toBe(1);
看起来不可能只在控制器的初始化进程中测试 scope。可以测试任何其他依赖项,因为事先知道它们的状态。
一些确认代码:
app.controller('zeppelin', ['$scope','hangman', function ($scope, hangman) {
$scope.love = function () {
hangman.a(4,6);
return 'sweet';
};
$scope.tangerine = $scope.love();
$scope.prove = function () {
$scope.love();
hangman.a(4,$scope.tangerine);
};
hangman.a(4,6);
}]);
测试代码:
beforeEach(inject(function (_$rootScope_, _$controller_, _hangman_) {
sandbox = sinon.sandbox.create();
scope = _$rootScope_.$new();
hangman = _hangman_;
blackDog = sinon.spy(hangman, 'a');
loved = sinon.spy(scope, 'love');
controller = _$controller_('zeppelin', {
$scope: scope,
hangman: hangman
});
proved = sinon.spy(scope, 'prove');
}));
it('should x', function () {
scope.prove();
scope.love();
expect(scope.tangerine).toBe('sweet');
expect(loved.callCount).toBe(2);
expect(blackDog.callCount).toBe(5);
expect(proved.callCount).toBe(1);
});
是否可以测试Angular控制器的init进程?
代码如下:
var app = angular.module('led', []);
app.controller('zeppelin', ['$scope', function ($scope) {
$scope.love = function () {
return 2;
};
$scope.tangerine = $scope.love();
$scope.prove = function () {
$scope.love();
};
$scope.prove();
$scope.love();
}]);
测试代码:
scope = _$rootScope_.$new();
controller = _$controller_('zeppelin', {
$scope: scope
});
proved = sinon.spy();
loved = sinon.stub().returns(7);
scope.prove = proved;
scope.love = loved;
expect(proved.callCount).toBe(1); //=> 0
expect(loved.callCount).toBe(2); //=> 0
scope.prove();
expect(proved.callCount).toBe(2); //=> 1
expect(loved.callCount).toBe(3); //=> 0
我对上述代码的问题是:
- 我知道预期会失败,因为控制器已经初始化。如果我在初始化之前设置 scope.prove = spied ,那么即使在显式调用它们之后,我也会收到 0 。是否有测试 init 进程的有效方法?
- 如何存根 scope.love,所以 scope.tangerine 从存根接收数据,而不是从存根接收数据控制器?
- 为什么调用scope.prove()后,loved.callCount不自增?
对我来说最奇怪的案例是 3,因为我想不出任何合理的解释来解释这种奇怪的行为。
1.
控制器只是一个构造函数,它接受 $scope
对象作为参数。您正在范围
$scope.love = function () {
return 2;
};
$scope.prove = function () {
$scope.love();
};
然后立即调用它们
$scope.prove();
$scope.love();
无法拦截此进程(在 Angular 中或一般情况下)以在控制器末尾首次调用函数之前监视这些函数。如果你在初始化控制器之前设置$scope.prove = spied
,那么它将被控制器简单地覆盖,如果你设置它之后然后调用函数时间谍还不存在。
2.
由于上述原因,您无法及时将 $scope.love
的 return 值分配给 $scope.tangerine
。如果能够在测试中修改控制器初始化行为对您来说真的很重要,您应该使 love
和可注入服务将其注入控制器,并在您的测试中模拟它。
3.
$scope.prove
被您的间谍覆盖 (scope.prove = proved
)。您正在丢失在您的控制器中创建的调用 $scope.love
的原始函数。相反,您需要监视现有功能。例如,
var controller = $controller('zeppelin', { $scope: scope });
// here, the controller has initialized scope.prove and scope.love
var loved = sinon.spy(scope, 'love');
var proved = sinon.spy(scope, 'prove');
scope.prove();
// both callCounts are incremented
expect(proved.callCount).toBe(1);
expect(loved.callCount).toBe(1);
看起来不可能只在控制器的初始化进程中测试 scope。可以测试任何其他依赖项,因为事先知道它们的状态。
一些确认代码:
app.controller('zeppelin', ['$scope','hangman', function ($scope, hangman) {
$scope.love = function () {
hangman.a(4,6);
return 'sweet';
};
$scope.tangerine = $scope.love();
$scope.prove = function () {
$scope.love();
hangman.a(4,$scope.tangerine);
};
hangman.a(4,6);
}]);
测试代码:
beforeEach(inject(function (_$rootScope_, _$controller_, _hangman_) {
sandbox = sinon.sandbox.create();
scope = _$rootScope_.$new();
hangman = _hangman_;
blackDog = sinon.spy(hangman, 'a');
loved = sinon.spy(scope, 'love');
controller = _$controller_('zeppelin', {
$scope: scope,
hangman: hangman
});
proved = sinon.spy(scope, 'prove');
}));
it('should x', function () {
scope.prove();
scope.love();
expect(scope.tangerine).toBe('sweet');
expect(loved.callCount).toBe(2);
expect(blackDog.callCount).toBe(5);
expect(proved.callCount).toBe(1);
});