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



我有以下代码要用Qunit进行测试。

// my code under test
document.getElementById('saveButton').addEventListener('click',save);
function save() {
console.log('save clicked');
}

我的 QUnit 测试获取对按钮的引用并调用单击函数:

(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,该事件确实会触发单击事件处理程序。

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。我还没有达到我可以或愿意更改测试代码的阶段。

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

如果在QUnit.test()中定义了btn,则它引用新的<button>,它没有分配事件侦听器。QUnit 预计主要与 jQuery 一起使用,因此它可能会执行 jQuery.clone(),可以将其设置为复制基于 jQuery 的事件侦听器,但不能复制原版 JavaScript 侦听器,因为底层Node.cloneNode()没有该功能。

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

QUnit 克隆 HTML 内容是处理彼此独立的测试的好方法,但应记录在案。通常希望单元测试是独立的。毕竟,可能会有更改 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;

文档说:

QUnit.config.fixture (string) | default: undefined

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

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

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

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

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

// 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>

或者,对预定义的事件侦听器使用 HTML 属性

对于您遇到的特定问题,即希望在测试之前设置事件侦听器,您可以使用 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>

使用HTML属性是Mat在聊天中提到的。

相关内容

  • 没有找到相关文章

最新更新