web-dev-qa-db-ja.com

一連の「AssertEquals」よりも単体テストを書くためのより良い方法はありますか?

Qunitを使用した、単体テストの基本的な例を以下に示します。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>

<link rel="stylesheet" href="qunit/qunit-1.13.0.css">
<script src = "qunit/qunit-1.13.0.js"></script>
<script src = "../js/fuzzQuery.js"></script>

<script>

test("Fuzz Query Basics", function()
        {
            equal(fuzzQuery("name:(John Smith)"), "name:(John~ Smith~)");
            equal(fuzzQuery("name:Jon~0.1"), "name:Jon~0.1");
            equal(fuzzQuery("Jon"), "Jon~");
            //etc

        }
    );

</script>
</head>
<body>
    <div id="qunit"></div>
</body>
</html>

今私はこれが少し反復的であると考えていました。

すべての入力/出力を配列に入れ、それをループすることができます。

test("Fuzz Query Basics", function()
        {
            var equals = [
                           ["name:(John Smith)", "name:(John~ Smith~)"],
                           ["name:Jon~0.1", "name:Jon~0.1"],
                           ["Jon", "Jon~"]
                           ];

            for (var i = 0; i<equals.length; i++)
                {
                    equal(fuzzQuery(equals[i][0]), equals[i][1]);               
                }

        }
    );

そして、これはうまくいきます。

この2番目の方法について私が考えることができる唯一の利点は、実際にequalを使用したくないことが判明した場合、その変更を1つの場所で行う方が簡単であることです。

読みやすさの点では、どちらかといえば決定的なものだとは思いませんが、おそらく2番目を優先します。

さらに抽象化すると、入力/出力ケースを個別のCSVファイルに入れることができ、変更が容易になる場合があります。

質問は-これらの種類の単体テストを書くための一般的な慣習は何ですか?

それらを配列に入れてはいけない理由はありますか?

12
dwjohnston

リファクタリングされたテストには匂いがあります: 条件付きテストロジック

テストで条件付きロジックを記述しないようにする理由は2つあります。 1つ目は、リンクされたxUnitパターンの記事で説明されているように、テストコードが正しいことを確信する能力を損なうことです。

2つ目は、テストの意味がわかりにくくなることです。 Test Methods と書くのは、特定の動作をテストするためのロジックを1か所に配置し、説明的な名前を付けられるようにするためです( Dan NorthのオリジナルのBDD記事 を参照してください)テストのための良い名前の価値の調査)。テストがforループのある単一の関数内に隠されている場合、それはリーダーのコードの意味を覆い隠します。読者はループを理解する必要があるだけでなく、ループ内でテストされているさまざまな動作すべてを精神的に解明する必要もあります。

いつものように、解決策は抽象化のレベルを上げることです。 xUnit.NET または Contexts のように parametrised tests を提供するテストフレームワークを使用します(免責事項:私はコンテキストを記述しました)。これにより、個別の動作のテストを個別に保ちながら、同じ動作の三角分割テストを自然な方法でグループ化できます。

8

データ駆動型単体テストが本当に必要なようです。 QUnitの使用について言及したので、パラメーター化されたテストを可能にするプラグインを見つけました。

https://github.com/AStepaniuk/qunit-parameterize

テストコード自体が条件付きでない限り、データ駆動型テストにイデオロギー的に問題はありません。テストコードを見ると、データドリブンテストの非常に良い候補のようです。

GitHub READMEのコード例:

QUnit
    .cases([
        { a : 2, b : 2, expectedSum : 4 },
        { a : 5, b : 5, expectedSum : 10 },
        { a : 40, b : 2, expectedSum : 42 }
    ])
    .test("Sum test", function(params) {
        var actualSum = sum(params.a, params.b);
        equal(actualSum, params.expectedSum);
    });
5
Greg Burghardt

保守しやすい配列を使用することで、繰り返しが少なくなります。私が使用したい1つのアプローチは、テストを調整、実行、アサートする別のメソッドを用意することですが、テストする入力パラメーターを受け入れるため、入力セットごとに1つのテストメソッドを使用します。

これにより、どのテスト/入力が失敗したかを即座に知ることができます。

1
Kevin

2つ目のアプローチが好きですが、2ポイント追加します

  • テストされたデータを格納するために配列を使用しないでください。インデックスでの作業はクリーンな方法ではないためです。
  • forループを使用しない

`

[
    {
        process: "name:(John Smith)",
        result: "name:(John~ Smith~)"
    },
    {
        process: "name:Jon~0.1", 
        result: "name:Jon~0.1"
    },
    {
        process: "Jon", 
        result: "Jon~"
    }
]
.forEach(function(data){

    var result = fuzzQuery(data.process);
    equal(result, data.result);
});

qunitについてはわかりませんが、優れたテストランナーでは、どの入力文字列が失敗したか、および期待される結果が何であるかが示されます

0
tenbits