尽管浏览器中使用了相同的代码,Jamsine仍会引发错误

时间:2018-08-31 19:29:00

标签: angularjs unit-testing jasmine document

我将此代码添加到我的组件控制器中以集中输入,它在浏览器中效果很好,但是破坏了我所有的模板测试。我以为我可以冲洗$timeout,一切都会好起来,但事实并非如此。

vm.$onInit = init;

function init(){
    focusInput();
}

function focusInput(){
    $timeout(function(){
        $document[0]
            .querySelector('md-autocomplete-wrap')
            .querySelector('input')
            .focus();
    }, 0);
}

但是,在我的单元测试中,Jasmine报告说.querySelector不可用,因为第一个querySelector的结果在测试环境中为空。

it('should render', function(){
    var wrap, searchBarDirective, $scope;
    $scope = $rootScope.$new();
    searchBarDirective = $compile(angular.element(template))($scope);
    $scope.$digest(); 

    $timeout.flush();

    wrap = searchBarDirective.find('md-autocomplete-wrap')[0];
    expect(wrap).toBeDefined();
});

对我来说很明显$document不包含呈现的指令,因此第二个querySelector失败了。但是,为什么$ document不包含指令?

我尝试用spyOn($document[0], "querySelector").and.returnValues($document[0],$document[0])嘲笑querySelector,但这并不能使我超越focus。想我在这里有很多路。

*修订版*

我认为继续使用$ document很重要,但是我决定为jqLit​​e querySelector方法删除find

function focusInput(){
    $timeout(function(){
        var input;
        try {
            // can throw an error if the first find fails
            input = $document.find('md-autocomplete').find('input');
        }
        catch (e) {
            angular.noop(e);
        }
        if(input && angular.isFunction(input.focus)) {
            input.focus();
        }
    }, 0);
}

我根据评论将测试更改为以下内容。我确实有Karma load jquery来简化测试,这使我可以搜索:focus

beforeEach(function(){
    element = angular.element(template);
    $document[0].body.appendChild(element[0]);
    $scope = $rootScope.$new();
});
afterEach(function(){
    element[0].remove();
});
it('should be focused', function(){
    var input, searchBarDirective;
    searchBarDirective = $compile(element)($scope);

    $scope.$digest();
    $timeout.flush();

    input = searchBarDirective.find(':focus')[0];
    expect(input).toBeDefined();
});

1 个答案:

答案 0 :(得分:1)

querySelector调用在浏览器中有效但在测试中无效的原因是,您正在使用angular.element创建DOM元素,但从未将其附加到文档中。有两种解决方法:

首先,您可以简单地执行此操作。代替:

searchBarDirective = $compile(angular.element(template))($scope);

要做:

let element; // declare this in the describe block so it is available later

element = angular.element(template);
document.body.appendChild(element[0]);
searchBarDirective = $compile(element)($scope);

然后执行以下操作:

afterEach(() => element[0].remove());

但是,这有点混乱。除非必须这样做,否则不应在单元测试中操纵全局范围(即文档)。在您的非测试代码中最好避免访问文档,而是访问一个scope元素,或者也可以在测试中模拟的其他DOM元素。这将很难完成,因为可能需要重新架构一下代码。不过,总的来说,为了制作模块化且可测试的代码,您要避免尽可能多地访问document对象。