如何在输入字段上对事件侦听器进行单元测试? [英] How can I unit test an event listener on an input field?

查看:54
本文介绍了如何在输入字段上对事件侦听器进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在最简单的测试中,我试图测试以下功能:

In the simplest test possible, I'm attempting to test the following function:

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")上的事件监听器.当用户在字段中键入一个值然后触发提交"事件(通过ENTER"按键)时,将调用addPercentSign"函数.

The above function is an event listener on an input field (id="learningCurveInput"). When a user types a value into the field and then triggers a 'submit' event (via "ENTER" keypress), the 'addPercentSign' function gets called.

我理解单元测试的想法是尽可能地将测试与任何依赖项隔离".因此,要针对 DOM 操作进行测试,可以将元素附加到 div 下的 test.html,如下所示:

I understand that with unit tests, the idea is to 'isolate' the test as much as possible from any dependencies. Therefore, to test against a DOM manipulation, one can attach the element to the test.html under a div like so:

$('<input id="learningCurveInput" type="text"/>').appendTo('#qunit-fixture');

谁能解释一下接下来要做什么?该函数依赖传入的 Event 对象来检索输入值.我不确定如何在测试中重新创建它.我在下面附上了我的单元测试,但这只是为了表明我的想法:

Can anyone explain what to do next here? The function relies on the Event object getting passed in to retrieve the input value. I'm not sure how to recreate that within the test. I've attached my unit test below, but it's just to show my thinking:

...,
    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?
        });

    }
);

总结问题

如何对在输入字段的提交"时调用的addPercentSign"函数进行单元测试?

How can I unit test the 'addPercentSign' function that is called on 'submit' of an input field?

推荐答案

我建议把它分成以下几部分:

I'd suggest splitting this up in these parts:

  • 测试从输入到输出的转换,即:"51" ->51.0%"
  • 测试您修改input值的方法是否有效
  • 测试是否在需要时调用了附加的事件侦听器

如果所有这些测试都成功,您可以假设将它们链接在一起也能正常工作.

If all of these tests succeed, you can assume chaining them together will work as well.

要测试转换方法,我建议将其逻辑移到单独的纯函数中.我在下面粘贴了您的格式逻辑并删除了 setValue 副作用.我包括了一些测试(你应该检查它们,看看你是否需要更多/它们符合你的要求).我以两个失败的测试为例.

To test the conversion method, I'd suggest moving its logic into a separate, pure function. I've pasted your format logic below and removed the setValue side effect. I included some tests (you should check them out and see if you need more/they match your requirements). I've left two failing tests as an example.

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>

既然您已经了解了这个方法,您需要了解是否可以从 检索值并将值写入 输入字段.这些与您已经写过的相似:

Now that you've got this method covered, you'll need to find out if you can retrieve values from and write values to an input field. Those are similar to the one you already wrote:

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) 正确读取和写入值,您将需要测试您是否获取值 -> 转换值 -> 设置值 序列将由正确的输入触发.例如:

Now that you know you can (a) correctly transform values, and (b) correctly read and write values, you'll need to test if your get value -> transform value -> set value sequence will be triggered by the correct input. For example:

jQuery 有一些方便的方法来附加和触发事件监听器.您可以使用不带参数的 .changesubmit 来模拟 UI 输入.或者,您可以在提交按钮上触发 click.

jQuery has some handy methods for attaching and triggering event listeners. You can use .change or submit without an argument to mimic UI input. Alternatively, you can trigger click on a submit button.

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>

现在,您可以确定您的测试是否涵盖了您的代码实现:

Now, you can determine if an implementation of your code is covered by your tests:

$("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() 之类的方法的测试:检查他们的公共存储库以查看这些方法的覆盖范围.如果您要实现与 DOM 交互的自定义​​方法,您确实需要测试它们.

Notice that it's mainly the first test module that has custom logic and requirements. If you're using jQuery, which is already heavily tested, you won't need to re-implement tests for methods such as .val(): check their public repo to see the coverage for those. If you're implementing custom methods to interact with the DOM, you do need to test them.

所以,简而言之:将 addPercentageSign 重写为一个纯函数,它接受一个字符串并返回一个字符串;确保它经过彻底测试.通过经过测试的库与 DOM 交互,或为 DOM 编辑和事件监听编写测试.

So, in short: rewrite addPercentageSign to be a pure function that takes a string and returns a string; make sure it's thoroughly tested. Interact with the DOM via a tested library, or write tests for both DOM editing and event listening.

这篇关于如何在输入字段上对事件侦听器进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆