在QUnit测试中,只有在测试运行之前获得引用时,才会触发click事件

问题描述 投票:6回答:1

我有以下代码,我想用Qunit测试。

// my code under test
document.getElementById('saveButton').addEventListener('click',save);

function save() {
  console.log('save clicked');
}

我的QUnit测试获得对按钮的引用并调用click函数:

(function () {
    "use strict";

    // HACK: with this line here click  works
    //var btn = document.getElementById('saveButton');

    // the test
    QUnit.test("click save", function (assert) {
         // with this line no click
        var btn = document.getElementById('saveButton');
        btn.click(); // will it click?
        assert.ok(btn !== null , 'button found');
    });
}());

奇怪的是,如果我在Qunit的测试方法中调用getElementById,则不会调用附加到按钮的事件处理程序。但是,如果我在测试函数之外调用getElementById,则事件会触发click事件处理程序。

什么是QUnit在那里阻止我的测试按预期工作,以及解决我面临的问题的正确/推荐方法是什么?

这是一个MCVE(也在JSFiddle上),演示了非工作版本。我已经评论了要改变什么以使点击工作(工作意味着在这里:输出文本到控制台)

// code under test
document.getElementById('saveButton').addEventListener('click',save);

function save() {
  console.log('save clicked');
}

// QUnit tests
(function () {
	"use strict";
  // with this line here click  works
  //var btn = document.getElementById('saveButton');
  QUnit.test("click save", function (assert) {
    // with this line no click, comment it to test the working variant
    var btn = document.getElementById('saveButton');
    btn.click(); // will it click?
    assert.ok(btn !== null , 'button found');
  });
}());
<script src="https://code.jquery.com/qunit/qunit-2.9.2.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture">
  <button id="saveButton">test</button>
</div>

值得注意的是,我明确地不在这里使用jQuery,因为我正在编写测试的代码也没有使用jQuery。我尚未处于可以或者愿意更改测试代码的阶段。

javascript event-handling qunit
1个回答
5
投票

当你执行QUnit.test()时,似乎克隆并替换了<div id="qunit-fixture">下面的DOM中的所有内容。这意味着您在该调用之前添加的事件侦听器位于与DOM中现在不同的<button>上。 btn.click();的作用是因为它在原始的<button>上发射了这个事件,你用原始的var btn =保存了一个参考。

如果在btn中定义了QUnit.test(),那么它引用新的<button>,它没有分配给它的事件监听器。 QUnit期望主要用于jQuery,因此它可能会执行jQuery .clone(),可以设置为复制基于jQuery的事件侦听器,但不能复制vanilla JavaScript侦听器,因为底层的Node.cloneNode()没有这种功能。

您可以通过btn中的console.log(btn.parentNode.parentNode);确认原始QUnit.test()与DOM断开连接。这为null输出btn,但是<body>中存在的QUnit.test()。为了证明这一点,在运行测试之前确定的btn在下面的代码中被分配给btnBeforeQUnit

对HTML内容进行CSS克隆是一种很好的方法,可以使测试彼此独立,但应该记录下来。单元测试通常是独立的。毕竟,可能存在更改DOM结构的测试,应该在测试之间恢复。

但是,出于某种原因,QUnit不会恢复QUnit.test()末尾的原始DOM元素。文档指出它应该在下一次测试之前重新克隆原始文件,但是在测试完成时它不会恢复原始文件。

除了btnBeforeQUnitbtn之外,btnAfterQUnitbtnDelayedAfterQUnit的第二级父级也输出到控制台,以更准确地演示DOM替换何时发生,同时执行提供给QUnit.test()的回调。

// code under test
document.getElementById('saveButton').addEventListener('click',save);

function save() {
  console.log('save clicked');
}

// QUnit tests
(function () {
	"use strict";
  // with this line here click  works
  var btnBeforeQUnit = document.getElementById('saveButton');
  QUnit.test("click save", function (assert) {
    // with this line no click, comment it to test the working variant
    var btn = document.getElementById('saveButton');
    var btnInsideQUnit = btn;
    btn.click(); // will it click?
    console.log('btnBeforeQUnit.parentNode.parentNode', btnBeforeQUnit.parentNode.parentNode);
    console.log('btnInsideQUnit.parentNode.parentNode', btnInsideQUnit.parentNode.parentNode)
    assert.ok(btn !== null , 'buttin found');
  });
  var btnAfterQUnit = document.getElementById('saveButton');
  console.log('btnAfterQUnit.parentNode.parentNode', btnAfterQUnit.parentNode.parentNode);
  setTimeout(function() {
    var btnDelayedAfterQUnit = document.getElementById('saveButton');
    console.log('btnDelayedAfterQUnit.parentNode.parentNode', btnAfterQUnit.parentNode.parentNode);
  }, 1000);
}());
<script src="https://code.jquery.com/qunit/qunit-2.9.2.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture">
  <button id="saveButton">test</button>
</div>

调整此行为

您可以通过将QUnit.config.fixture设置为null来获得您期望的行为:

QUnit.config.fixture = null;

documentation says

QUnit.config.fixture(string)|默认值:undefined

定义要在每个测试开始时重置的fixture容器中使用的HTML内容。

默认情况下,QUnit将使用#qunit-fixture的起始内容作为夹具重置。如果您不希望在测试之间重置夹具,请将该值设置为null。

但是,在将此选项设置为null时应小心。这意味着DOM不能独立于所有在设置为null时运行的测试。换句话说,在一次测试中对DOM所做的更改将影响其他测试中的更改。

IMO,QUnit.test()在克隆DOM中的整体行为尚未明确记录。肯定应该在主文档中提到一些行为。尤其是副作用。应明确提到现有的事件监听器,但我没有在QUinit文档中找到任何明确描述此过程及其效果的内容。

以下是相同的代码,但添加了QUnit.config.fixture = null;。如您所见,“save clicked”从测试输出到控制台,而不是原始输出。

// code under test
document.getElementById('saveButton').addEventListener('click',save);

function save() {
  console.log('save clicked');
}

// QUnit tests
(function () {
	"use strict";
  QUnit.config.fixture = null;
  // with this line here click  works
  var btnBeforeQUnit = document.getElementById('saveButton');
  QUnit.test("click save", function (assert) {
    // with this line no click, comment it to test the working variant
    var btn = document.getElementById('saveButton');
    var btnInsideQUnit = btn;
    btn.click(); // will it click?
    console.log('btnBeforeQUnit.parentNode.parentNode', btnBeforeQUnit.parentNode.parentNode);
    console.log('btnInsideQUnit.parentNode.parentNode', btnInsideQUnit.parentNode.parentNode)
    assert.ok(btn !== null , 'buttin found');
  });
  var btnAfterQUnit = document.getElementById('saveButton');
  console.log('btnAfterQUnit.parentNode.parentNode', btnAfterQUnit.parentNode.parentNode);
  setTimeout(function() {
    var btnDelayedAfterQUnit = document.getElementById('saveButton');
    console.log('btnDelayedAfterQUnit.parentNode.parentNode', btnAfterQUnit.parentNode.parentNode);
  }, 1000);
}());
<script src="https://code.jquery.com/qunit/qunit-2.9.2.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture">
  <button id="saveButton">test</button>
</div>

Alternately, use HTML attributes for your pre-defined event listeners

对于您所遇到的特定问题,您希望在测试之前设置事件侦听器,您可以使用HTML属性来定义侦听器(例如,在您的情况下为onclick="save()")。

// code under test
document.getElementById('saveButton').addEventListener('click',save);

function save() {
  console.log('save clicked');
}

// QUnit tests
(function () {
	"use strict";
  // with this line here click  works
  var btnBeforeQUnit = document.getElementById('saveButton');
  QUnit.test("click save", function (assert) {
    // with this line no click, comment it to test the working variant
    var btn = document.getElementById('saveButton');
    var btnInsideQUnit = btn;
    btn.click(); // will it click?
    console.log('btnBeforeQUnit.parentNode.parentNode', btnBeforeQUnit.parentNode.parentNode);
    console.log('btnInsideQUnit.parentNode.parentNode', btnInsideQUnit.parentNode.parentNode)
    assert.ok(btn !== null , 'buttin found');
  });
  var btnAfterQUnit = document.getElementById('saveButton');
  console.log('btnAfterQUnit.parentNode.parentNode', btnAfterQUnit.parentNode.parentNode);
  setTimeout(function() {
    var btnDelayedAfterQUnit = document.getElementById('saveButton');
    console.log('btnDelayedAfterQUnit.parentNode.parentNode', btnAfterQUnit.parentNode.parentNode);
  }, 1000);
}());
<script src="https://code.jquery.com/qunit/qunit-2.9.2.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture">
  <button id="saveButton" onclick="save()">test</button>
</div>

Mat in chat提到了使用HTML属性。

© www.soinside.com 2019 - 2024. All rights reserved.