【问题标题】:Testing controller init process in Angular unit test在 Angular 单元测试中测试控制器初始化过程
【发布时间】:2015-08-13 20:59:41
【问题描述】:

是否可以测试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

我对上述代码的问题是:

  1. 我了解预期将失败,因为控制器已经初始化。如果我在初始化之前设置了 scope.prove = spied,那么即使在显式调用它们之后,我也会收到两者的 0。是否有测试 init 进程的可行方法?
  2. 如何存根 scope.love,以便 scope.tangerine 从存根而不是控制器接收数据?
  3. 为什么在调用 scope.prove() 后,loved.callCount 没有增加?

对我来说,最奇特的案例是 3,因为我想不出任何合乎逻辑的解释来解释这种奇怪的行为。

【问题讨论】:

  • 对于案例 3 - 当控制器初始化时 $scope.prove 持有对匿名函数 function () { $scope.love(); } 的引用,这将被 sinon.spy 返回的间谍函数替换,这是一个空函数不打电话给$scope.loved。您应该将现有的间谍包装在间谍中,而不是创建新的间谍。 Ej。 sinon.spy($scope, 'prove');
  • 谢谢!看来我没有正确设置间谍功能,因为我认为两种语法是相等的。

标签: angularjs unit-testing sinon


【解决方案1】:

1.

控制器只是一个构造函数,它接受$scope 对象作为参数。您正在范围上定义函数

$scope.love = function () {
    return 2;
};
$scope.prove = function () {
    $scope.love();
};

然后立即调用它们

$scope.prove();
$scope.love();

没有办法拦截这个过程(在 Angular 中,或者在一般情况下)以在函数在控制器末尾首次调用之前监视它们。如果你在初始化控制器之前设置$scope.prove = spied,那么它将被控制器简单地覆盖,如果你设置它在之后,那么当函数时间谍将不存在被调用。

2.

由于上述原因,您无法及时存根$scope.love,以便将其返回值分配给$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);

【讨论】:

  • 非常感谢,伙计!没想到(receivingAnswer()).toBeSuperTruthy() :)
【解决方案2】:

看起来不可能只在控制器的初始化进程上测试 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);
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-14
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多