ren*_*ene 6 javascript event-handling qunit
我有以下代码要与Qunit一起测试。
// my code under test
document.getElementById('saveButton').addEventListener('click',save);
function save() {
console.log('save clicked');
}
Run Code Online (Sandbox Code Playgroud)
我的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');
});
}());
Run Code Online (Sandbox Code Playgroud)
奇怪的是,如果我getElementById在Qunit的测试方法中调用附加在按钮上的事件处理程序,则不会被调用。但是,如果我将调用移到getElementById测试功能之外,则该事件会触发click事件处理程序。
QUnit在做什么,这会阻止我的测试按预期方式工作?解决我所面临的问题的正确/推荐方法是什么?
这是演示非工作版本的MCVE(也在JSFiddle上)。我已对更改内容进行了评论以使点击生效(此处的工作方式是:将文本输出到控制台)
// my code under test
document.getElementById('saveButton').addEventListener('click',save);
function save() {
console.log('save clicked');
}
Run Code Online (Sandbox Code Playgroud)
(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');
});
}());
Run Code Online (Sandbox Code Playgroud)
值得注意的是,我在这里明确不使用jQuery,因为我正在为其编写测试的代码也不使用jQuery。我尚未达到可以或愿意更改测试代码的阶段。
执行时QUnit.test(),似乎<div id="qunit-fixture">会复制并替换下面DOM中的所有内容。这意味着您在该调用之前添加的事件侦听器<button>与DOM中存在的侦听器不同。之所以btn.click();有效,是因为它在原始文件上触发了事件<button>,并且您已将原始文件保存到其中var btn =。
如果btn在中定义了QUnit.test(),则它引用新的<button>,没有为其分配事件监听器。QUnit预计将主要与jQuery一起使用,因此它可能会使用jQuery .clone(),可以将其设置为复制基于jQuery的事件侦听器,但不能复制普通的JavaScript侦听器,因为底层Node.cloneNode()没有此功能。
可以确认,原来btn是从DOM通过断开console.log(btn.parentNode.parentNode);内QUnit.test()。输出null原始btn,但它是<body>为中存在的一个QUnit.test()。为了证明这一点,在下面的代码中将btn确定在运行测试之前确定的值btnBeforeQUnit。
QUnit克隆HTML内容是一种使测试彼此独立的好方法,但应记录在案。通常希望单元测试是独立的。毕竟,可能会有改变DOM结构的测试,应该在测试之间进行恢复。
但是,由于某些原因,QUnit不会在末尾恢复原始DOM元素QUnit.test()。该文档表明,它应该在下一次测试之前重新克隆原始文件,但是在完成测试后它不会还原原始文件。
除了btnBeforeQUnit和btn,所述第二电平的父母btnAfterQUnit和btnDelayedAfterQUnit也被输出到控制台,当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);
}());Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
您可以通过设置QUnit.config.fixture为来获得预期的行为null:
QUnit.config.fixture = null;
Run Code Online (Sandbox Code Playgroud)
该文件说:
QUnit.config.fixture(字符串)| 默认值:未定义
定义在夹具容器中使用的HTML内容,该容器在每次测试开始时都会重置。
默认情况下,QUnit将使用#qunit-fixture的任何起始内容作为灯具重置。如果您不希望在两次测试之间重设灯具,请将值设置为null。
但是,将此选项设置为时应格外小心null。这将意味着DOM对于设置为的所有测试都不是独立的null。换句话说,您在一个测试中对DOM所做的更改将影响其他测试中的更改。
IMO,QUnit.test()克隆DOM 的总体行为没有明确记录。绝对应该在主文档中提及该行为。特别是副作用。应该显式地提及现有的事件侦听器,但是在QUinit文档中没有任何内容明确地描述了此过程及其影响。
以下是相同的代码,但QUnit.config.fixture = null;添加了代码。如您所见,“单击保存”从测试中输出到控制台,而不是原始输出。
QUnit.config.fixture = null;
Run Code Online (Sandbox Code Playgroud)
// 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);
}());Run Code Online (Sandbox Code Playgroud)
对于遇到的特定问题(想要在测试之前设置事件侦听器),可以使用HTML属性来定义侦听器(例如,onclick="save()"在您的情况下)。
<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>Run Code Online (Sandbox Code Playgroud)
// 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);
}());Run Code Online (Sandbox Code Playgroud)