如何在输入字段上对事件侦听器进行单元测试?
How can I unit test an event listener on an input field?
在最简单的测试中,我尝试测试以下函数:
addPercentSign: function (oEvent, control) {
var inputVal = oEvent.getParameters().value;
var inputNumber = parseFloat(inputVal);
if (inputNumber) {
if (inputNumber < 50 || inputNumber > 100) {
//see learningCurveFormatCheck
return null;
} else {
var finalVal = inputNumber.toFixed(1);
var finalOutput = finalVal + "%";
control.learningCurve.setValue(finalOutput);
return finalOutput;
};
}
}
上述函数是输入字段 (id="learningCurveInput") 上的事件侦听器。当用户在字段中键入一个值然后触发 'submit' 事件(通过 "ENTER" 按键)时,将调用 'addPercentSign' 函数。
我理解单元测试的想法是 'isolate' 尽可能多地从任何依赖项进行测试。因此,要针对 DOM 操作进行测试,可以将元素附加到 div 下的 test.html,如下所示:
$('<input id="learningCurveInput" type="text"/>').appendTo('#qunit-fixture');
任何人都可以在这里解释下一步该做什么吗?该函数依赖于传入的 Event 对象来检索输入值。我不确定如何在测试中重新创建它。我在下面附上了我的单元测试,但这只是为了展示我的想法:
...,
function (formatter, viewControls) {
"use strict";
QUnit.module("Formatter Object Exists")
QUnit.test("Learning Curve Input Value", function (assert) {
$('<input id="learningCurveInput" type="text"/>').appendTo('#qunit-fixture');
$("#learningCurveInput").val("55");
var result = '55';
equals(result, $('#learningCurveInput').val(), "testing input value");
});
QUnit.test("addPecentSign Function", function (assert) {
//how to test this dom-dependent function?
});
}
);
总结性问题
如何对在输入字段的 'submit' 上调用的 'addPercentSign' 函数进行单元测试?
我建议将它分成这些部分:
- 测试从输入到输出的转换,即:
"51" -> "51.0%"
- 测试您修改
input
值的方法是否有效
- 测试您的附加事件侦听器是否在需要时被调用
如果所有这些测试都成功,您可以假设将它们链接在一起也能正常工作。
为了测试转换方法,我建议将其逻辑移动到一个单独的纯函数中。我在下面粘贴了您的格式逻辑并删除了 setValue
副作用。我包括了一些测试(你应该检查它们,看看你是否需要 more/they 符合你的要求)。我留下了两个失败的测试作为例子。
字符串格式测试:
function addPercentSign(val) {
var inputNumber = parseFloat(val);
if (inputNumber) {
if (inputNumber < 50 || inputNumber > 100) {
//see learningCurveFormatCheck
return null;
} else {
var finalVal = inputNumber.toFixed(1);
var finalOutput = finalVal + "%";
return finalOutput;
};
};
};
module("String formatting");
test("Returns undefined for unparseable strings", function() {
["foo", null, NaN, "0.0.0"]
.forEach(function(result, i, arr) {
var result = addPercentSign(result);
strictEqual(result, undefined, arr[i] + " produces " + result);
});
});
test("Returns null for values below 50 and above 100", function() {
["0", "0.0", "25", "49.99999", "100.00000001", "10300", Infinity]
.forEach(function(result, i, arr) {
var result = addPercentSign(result);
strictEqual(result, null, arr[i] + " produces " + result);
});
});
test("Appends a % sign for values between 50 and 100", function() {
strictEqual(addPercentSign("51.0"), "51.0%");
// ...
});
test("Appends a digit for values without one", function() {
strictEqual(addPercentSign("51"), "51.0%");
// ...
});
test("Removes and rounds digits for values with more than one", function() {
strictEqual(addPercentSign("51.999"), "52.0%");
strictEqual(addPercentSign("51.06"), "51.1%");
// ...
});
<link href="https://code.jquery.com/qunit/qunit-1.12.0.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/qunit/qunit-1.12.0.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
现在您已经了解了这个方法,您需要了解是否可以从 检索值 并将值 写入 一个输入字段。这些与您已经写过的相似:
获取和设置输入的value
function createInput() {
return $('<input id="learningCurveInput" type="text" value="hello world"/>')
.appendTo('#qunit-fixture');
}
module("Writing and reading to input", {})
test("writes to value", function(assert) {
var $input = createInput();
var result = "55";
// Whatever you're using to set:
// in your code, I read `control.learningCurve.setValue`
// if that's what you're using, that's the method you should test
$input.val(result);
strictEqual($input.val(), result);
});
test("reads from value", function(assert) {
var $input = createInput();
// Whatever you're using to get the value
$input.val();
strictEqual($input.val(), "hello world");
});
<link href="https://code.jquery.com/qunit/qunit-1.12.0.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/qunit/qunit-1.12.0.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
现在您知道您可以 (a) 正确地转换值,以及 (b) 正确地读取和写入值,您需要测试您的 get value -> transform value -> set value 序列将由正确的输入触发。例如:
测试事件侦听器
jQuery 有一些方便的方法来附加和触发事件侦听器。您可以使用不带参数的 .change
或 submit
来模拟 UI 输入。或者,您可以在提交按钮上触发 click
。
function createForm() {
return $("<form></form>")
.append(createInput());
}
function createInput() {
return $('<input id="learningCurveInput" type="text" value="hello world"/>')
.appendTo('#qunit-fixture');
}
module("Form event listeners", {})
test("input executes method on change", function(assert) {
var called = false;
var onChange = function() { called = true; };
var $input = createInput();
$input.change(onChange);
$input.val("test");
strictEqual(called, false);
$input.change();
strictEqual(called, true);
});
test("form executes method on submit", function(assert) {
var called = false;
var onSubmit = function() { called = true; };
var $form = createForm();
var $input = $form.find("input");
$form.submit(onSubmit);
strictEqual(called, false);
$form.submit();
strictEqual(called, true);
});
<link href="https://code.jquery.com/qunit/qunit-1.12.0.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/qunit/qunit-1.12.0.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
总结
现在,您可以确定您的代码的实现是否包含在您的测试中:
$("form").submit(function() { // Tested in test 3
var $input = $(this).find("input");
var originalValue = $input.val(); // Tested in test 2
var newValue = addPercentSign(originalValue); // Tested in test 1
$input.val(newValue); // Tested in test 2
});
请注意,它主要是第一个具有自定义逻辑和要求的测试模块。如果您使用的是已经过大量测试的 jQuery,则无需为 .val()
等方法重新实施测试:检查他们的 public 存储库以查看对那些。如果您要实现与 DOM 交互的自定义方法,您 需要测试它们。
所以,简而言之:将 addPercentageSign
重写为一个接受字符串和 returns 字符串的纯函数;确保它经过彻底测试。通过经过测试的库与 DOM 交互,或者为 DOM 编辑和事件监听编写测试。
在最简单的测试中,我尝试测试以下函数:
addPercentSign: function (oEvent, control) {
var inputVal = oEvent.getParameters().value;
var inputNumber = parseFloat(inputVal);
if (inputNumber) {
if (inputNumber < 50 || inputNumber > 100) {
//see learningCurveFormatCheck
return null;
} else {
var finalVal = inputNumber.toFixed(1);
var finalOutput = finalVal + "%";
control.learningCurve.setValue(finalOutput);
return finalOutput;
};
}
}
上述函数是输入字段 (id="learningCurveInput") 上的事件侦听器。当用户在字段中键入一个值然后触发 'submit' 事件(通过 "ENTER" 按键)时,将调用 'addPercentSign' 函数。
我理解单元测试的想法是 'isolate' 尽可能多地从任何依赖项进行测试。因此,要针对 DOM 操作进行测试,可以将元素附加到 div 下的 test.html,如下所示:
$('<input id="learningCurveInput" type="text"/>').appendTo('#qunit-fixture');
任何人都可以在这里解释下一步该做什么吗?该函数依赖于传入的 Event 对象来检索输入值。我不确定如何在测试中重新创建它。我在下面附上了我的单元测试,但这只是为了展示我的想法:
...,
function (formatter, viewControls) {
"use strict";
QUnit.module("Formatter Object Exists")
QUnit.test("Learning Curve Input Value", function (assert) {
$('<input id="learningCurveInput" type="text"/>').appendTo('#qunit-fixture');
$("#learningCurveInput").val("55");
var result = '55';
equals(result, $('#learningCurveInput').val(), "testing input value");
});
QUnit.test("addPecentSign Function", function (assert) {
//how to test this dom-dependent function?
});
}
);
总结性问题
如何对在输入字段的 'submit' 上调用的 'addPercentSign' 函数进行单元测试?
我建议将它分成这些部分:
- 测试从输入到输出的转换,即:
"51" -> "51.0%"
- 测试您修改
input
值的方法是否有效 - 测试您的附加事件侦听器是否在需要时被调用
如果所有这些测试都成功,您可以假设将它们链接在一起也能正常工作。
为了测试转换方法,我建议将其逻辑移动到一个单独的纯函数中。我在下面粘贴了您的格式逻辑并删除了 setValue
副作用。我包括了一些测试(你应该检查它们,看看你是否需要 more/they 符合你的要求)。我留下了两个失败的测试作为例子。
字符串格式测试:
function addPercentSign(val) {
var inputNumber = parseFloat(val);
if (inputNumber) {
if (inputNumber < 50 || inputNumber > 100) {
//see learningCurveFormatCheck
return null;
} else {
var finalVal = inputNumber.toFixed(1);
var finalOutput = finalVal + "%";
return finalOutput;
};
};
};
module("String formatting");
test("Returns undefined for unparseable strings", function() {
["foo", null, NaN, "0.0.0"]
.forEach(function(result, i, arr) {
var result = addPercentSign(result);
strictEqual(result, undefined, arr[i] + " produces " + result);
});
});
test("Returns null for values below 50 and above 100", function() {
["0", "0.0", "25", "49.99999", "100.00000001", "10300", Infinity]
.forEach(function(result, i, arr) {
var result = addPercentSign(result);
strictEqual(result, null, arr[i] + " produces " + result);
});
});
test("Appends a % sign for values between 50 and 100", function() {
strictEqual(addPercentSign("51.0"), "51.0%");
// ...
});
test("Appends a digit for values without one", function() {
strictEqual(addPercentSign("51"), "51.0%");
// ...
});
test("Removes and rounds digits for values with more than one", function() {
strictEqual(addPercentSign("51.999"), "52.0%");
strictEqual(addPercentSign("51.06"), "51.1%");
// ...
});
<link href="https://code.jquery.com/qunit/qunit-1.12.0.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/qunit/qunit-1.12.0.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
现在您已经了解了这个方法,您需要了解是否可以从 检索值 并将值 写入 一个输入字段。这些与您已经写过的相似:
获取和设置输入的value
function createInput() {
return $('<input id="learningCurveInput" type="text" value="hello world"/>')
.appendTo('#qunit-fixture');
}
module("Writing and reading to input", {})
test("writes to value", function(assert) {
var $input = createInput();
var result = "55";
// Whatever you're using to set:
// in your code, I read `control.learningCurve.setValue`
// if that's what you're using, that's the method you should test
$input.val(result);
strictEqual($input.val(), result);
});
test("reads from value", function(assert) {
var $input = createInput();
// Whatever you're using to get the value
$input.val();
strictEqual($input.val(), "hello world");
});
<link href="https://code.jquery.com/qunit/qunit-1.12.0.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/qunit/qunit-1.12.0.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
现在您知道您可以 (a) 正确地转换值,以及 (b) 正确地读取和写入值,您需要测试您的 get value -> transform value -> set value 序列将由正确的输入触发。例如:
测试事件侦听器
jQuery 有一些方便的方法来附加和触发事件侦听器。您可以使用不带参数的 .change
或 submit
来模拟 UI 输入。或者,您可以在提交按钮上触发 click
。
function createForm() {
return $("<form></form>")
.append(createInput());
}
function createInput() {
return $('<input id="learningCurveInput" type="text" value="hello world"/>')
.appendTo('#qunit-fixture');
}
module("Form event listeners", {})
test("input executes method on change", function(assert) {
var called = false;
var onChange = function() { called = true; };
var $input = createInput();
$input.change(onChange);
$input.val("test");
strictEqual(called, false);
$input.change();
strictEqual(called, true);
});
test("form executes method on submit", function(assert) {
var called = false;
var onSubmit = function() { called = true; };
var $form = createForm();
var $input = $form.find("input");
$form.submit(onSubmit);
strictEqual(called, false);
$form.submit();
strictEqual(called, true);
});
<link href="https://code.jquery.com/qunit/qunit-1.12.0.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/qunit/qunit-1.12.0.js"></script>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
总结
现在,您可以确定您的代码的实现是否包含在您的测试中:
$("form").submit(function() { // Tested in test 3
var $input = $(this).find("input");
var originalValue = $input.val(); // Tested in test 2
var newValue = addPercentSign(originalValue); // Tested in test 1
$input.val(newValue); // Tested in test 2
});
请注意,它主要是第一个具有自定义逻辑和要求的测试模块。如果您使用的是已经过大量测试的 jQuery,则无需为 .val()
等方法重新实施测试:检查他们的 public 存储库以查看对那些。如果您要实现与 DOM 交互的自定义方法,您 需要测试它们。
所以,简而言之:将 addPercentageSign
重写为一个接受字符串和 returns 字符串的纯函数;确保它经过彻底测试。通过经过测试的库与 DOM 交互,或者为 DOM 编辑和事件监听编写测试。