我怎样才能存根有问题的代码[单元测试]

How can I stub a problematic piece of code [Unit Testing]

编写单元测试的新手,'mocking' 的概念更准确。我有一个基本函数 'addPercentSign',如果它在 50-100 之间,它会向用户输入添加一个百分比字符:

    addPercentSign: function (oEvent, control) {
        var inputVal = oEvent.getParameters().value;
        var inputNumber = parseFloat(inputVal);

        if (inputNumber) {

            if (inputNumber < 50 || inputNumber > 100) {
                return null;
            } else {
                var finalVal = inputNumber.toFixed(1);
                var finalOutput = finalVal + "%";

//Error: cannot setValue of undefined. How can I 'stub' the line below?
                control.learningCurve.setValue(finalOutput);

                return finalOutput;
            };
        }
    }

问题

我面临的问题是,当我为此函数编写单元测试时,我无法测试返回值 (finalOutput),因为它上面的行 returns 是一个错误,因为它依赖于 DOM 元素 (control.learningCurve) 设置一个值。由于这是一个单元测试,我必须将此函数与任何依赖项隔离开来。

因此,我需要'mock''setValue'调用。我认为创建一个存根是有意义的,但我不确定如何做?

这是我要测试上述功能 (addPercentSign) 的单元测试代码:

function (formatter, viewControls) {
    "use strict";

    var testEvent = {
        getParameters : function() {
                return {value : 50}
        }
    }

    QUnit.module("Formatter Functions");
    QUnit.test("Add Percent Sign", function (assert) {

    assert.ok(formatter.addPercentSign(testEvent, viewControls) == '50.0%', "Percent Sign Added: Pass");

    });
}

问题

我如何模拟 setter 以便我可以在没有 DOM 依赖项的情况下对该函数进行单元测试?:

control.learningCurve.setValue(finalOutput)

Sinon可以作为测试替身

此示例使用的是 sinon 存根。

sinon.stub(control.learningCurve, 'setValue').returns('value that you need');

编辑:

function (formatter, viewControls) {
"use strict";

    sinon.stub(control.learningCurve, 'setValue').returns('value that you need');

    var testEvent = {
        getParameters : function() {
                return {value : 50}
        }
    }

    QUnit.module("Formatter Functions");
    QUnit.test("Add Percent Sign", function (assert) {

    assert.ok(formatter.addPercentSign(testEvent, viewControls) == '50.0%', "Percent Sign Added: Pass");

    });
}

我不确定,因为我不是 JS 忍者,但我认为它不应该那么难。

您要使用的方法位于名称为 'learningCurve' 的对象内,该对象位于 'control' 对象内。

如果控制权在你的格式化程序中,你不能这样做吗:

formatter.control = {};
formatter.control.learningCurve = {};
formatter.control.learningCurve.setValue = function() {
    return something_to_return;
}   

assert.ok(formatter.addPercentSign(testEvent, viewControls) == '50.0%', "Percent Sign Added: Pass");

如果它不在格式化程序内部,那么通过创建一个新的控件实例并像上面那样替换里面的内容来做到这一点。

我发现单元测试的主要附加值之一是它让您意识到代码可以重构的地方。在您的情况下,有两个不同的问题:附加 % 和修改 DOM。通过将这 2 个问题重构为各自的功能,您可以在不模拟任何内容的情况下对您的逻辑进行单元测试。

//Formerly addPercentSign
inputChanged: function (oEvent, control) {
    var inputVal = oEvent.getParameters().value;
    var inputNumber = parseFloat(inputVal);
    var formattedNumber = addPercentSign(inputNumber);
    control.learningCurve.setValue(formattedNumber);
}

function addPercentSign(inputNumber) {
    if (inputNumber < 50 || inputNumber > 100) {
        return inputNumber;
    }
    var finalVal = inputNumber.toFixed(1);
    return finalVal + "%";
}

现在您可以轻松测试 addPercentSign 了。您也可以对 inputChanged 进行单元测试,但这只是对 javascript 框架进行单元测试。