一連の「AssertEquals」よりも優れた単体テストの作成方法はありますか?


12

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番目の方法で考えられる1つの利点は、実際に使用equalしたくないことが判明した場合、その変更を1つの場所で行う方が簡単だということです。

読みやすさの点では、どちらの方法も決定的ではないと思いますが、おそらく2番目の方法を好むでしょう。

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

質問です-これらの種類の単体テストの作成に関する一般的な規則は何ですか?

配列に入れない理由はありますか?


これらのいずれかが失敗した値を教えてくれますか?
ジェフ14年

1
@JeffO-はい-QUnitを使用-テストが失敗した場合、出力には期待値と実際の値が表示されます。
dwjohnston 14年

回答:


8

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

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

2番目は、テストの意味を曖昧にすることです。テストメソッドを記述するのは、特定の動作をテストするためのロジックを1か所に配置し、わかりやすい名前を付けることができるためです(テストに適した名前の価値の調査については、Dan Northの元のBDDの記事を参照してください)。テストがforループのある単一の関数内に隠されていると、読者にとってコードの意味がわかりにくくなります。読者はループを理解する必要があるだけでなく、ループ内でテストされているすべての異なる動作を精神的に解明する必要もあります。

解決策は、いつものように、抽象化のレベルを上げることです。xUnit.NETContextsが行うように、パラメータ化されたテストを提供するテストフレームワークを使用します(免責事項:コンテキストを作成しました)。これにより、同じ振る舞いの三角測量テストを自然な方法でグループ化しながら、別々の振る舞いのテストを別々に保つことができます。


ところで、良い質問
ベンジャミンホジソン14年

1
1)抽象化のレベルを上げた場合、forループによって隠されていると言ったのとまったく同じ詳細を非表示にしませんか?2)ここでパラメーター化されたテストが適用できるかどうかわからない。ここにはどこかに類似点があるように見えますが、10〜20個の値のデータセットがあり、それらすべてをSUTで実行したいOPに似た状況がたくさんありました。はい、各値は異なり、異なる境界をテストする可能性がありますが、実際にはすべての単一値のテスト名を「発明」するように思えますが、やり過ぎです。私は...同様のを使用して最適値/コード・サイズ比を発見した
DXM

...ループ。テストが失敗した場合、アサートは失敗したものを正確に出力し、開発者は問題を正確に特定するのに十分なフィードバックを得ることができます。
DXM 14年

@DXM 1)テストフレームワークは、パラメータ化されたテスト機能を提供します。テストフレームワークを暗黙的に信頼しているため、テストフレームワークを作成しません。2)パラメータ化されたテストは、まさにこの目的のためです。毎回まったく同じ手順を実行しますが、異なる入力/出力値を使用します。テストフレームワークを使用すると、同じテストメソッドで異なる入力を実行することで、それぞれに名前を書く必要がなくなります。
ベンジャミンホジソン14年

5

データドリブンユニットテストが本当に必要なようです。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);
    });

1
同意すると、データ駆動テストのように見えます。しかし、それは彼が2番目のコード例で既に持っているもののようです。
ロバートハーベイ14

1
@RobertHarvey-正しい。彼が達成しようとしていることには受け入れられている用語があり、これらの種類のテストをより簡単に作成するために使用されているテストフレームワーク用のプラグインが存在します。将来の答えに注目する価値があると思いました、それだけです。
グレッグブルクハート14

1

より保守しやすい配列を使用することで、繰り返しの回数を減らしています。私が使用したい方法の1つは、テストを配置、実行、およびアサートする別個のメソッドを使用することですが、テスト中の入力パラメーターを受け入れるため、入力セットごとに1つのテストメソッドがあります。

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


0

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についてはわかりませんが、優れたテストランナーは、どの入力文字列が失敗し、どのような結果が期待されたかを示します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.