在AngularJS事件单元测试中模拟事件对象

9

我有以下测试:

    it('Should keep location when user rejects confirmation', inject(function ($controller, $rootScope) {
        var confirmStub = sinon.stub(),
            eventStub = {
                preventDefault: sinon.spy()
            };

        miscServiceStub = function () {
            this.confirm = confirmStub;
        };

        confirmStub.returns(false);

        initializeController($controller, 'Builder', $rootScope);

        $rs.$broadcast('$locationChangeStart', eventStub);
        expect(confirmStub).toHaveBeenCalledOnce();
        expect(confirmStub).toHaveBeenCalledWith('Are you sure you want to leave? you will loose any unsaved changes.');
        expect(eventStub.stopPropagation).toHaveBeenCalledOnce();

        miscServiceStub = function () {};
    }));

测试这个代码的方式:

$rootScope.$on('$locationChangeStart', function (event) {
    dump(arguments);
    if (!$miscUtils.confirm('Are you sure you want to leave? you will loose any unsaved changes.')){
        event.preventDefault();
    }
});

event.$stopPropagation 不会调用模拟的事件,而 dump(arguments) 显示实际事件对象后面传递的是它。

Chromium 31.0.1650 (Ubuntu) DUMP: Object{
    0: Object{name: '$locationChangeStart', targetScope: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., activeTab: ..., routeParams: ...}, preventDefault: function () { ... }, defaultPrevented: false, currentScope: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., activeTab: ..., routeParams: ...}}, 
    1: Object{stopPropagation: spy}
}

我应该如何使事件对象成为模拟事件,而不是真正的事件对象本身?我的方法正确吗?我对Angular还很陌生,对代码/测试的任何评论都将不胜感激。

如果需要更多相关代码,请告诉我。

2个回答

37

$scope.$broadcast会返回Event对象,因此你可以这样做:

var event = $scope.$broadcast("someEvent");
expect(event.defaultPrevented).toBeTruthy();

3
在这行中:
$rs.$broadcast('$locationChangeStart', eventStub);

您提供的参数将与事件一起传输,而不是事件本身。这就是为什么您会在此处获得:

$rootScope.$on('$locationChangeStart', function (event) 

需要传入两个对象作为参数。你的事件的完整签名应该是:

$rootScope.$on('$locationChangeStart', function (event, eventStub) 

所以: 你无法以你试过的方式测试stopPropagation的调用。

如果你查看angular源代码(第12185行...),你会发现事件是在没有任何可模拟这个对象的可能性下创建的。而且$scope本身也不被angular-mock模拟。

如果想要测试preventDefault是否被调用,我会编写一个调用preventDefault的函数的服务。这个服务可以很容易地被spy。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接