ユニットテスト

最初にすべきことは、プログラムをコンパイルして実行し、テストが実際に動作し、確かに失敗することを確認することだ。

dmd deneme.d -w -unittest
./deneme
core.exception.AssertError@deneme(11): ユニットテストの失敗
Bash

行番号11は、最初のテストが失敗したことを示している。

説明のために、偶然最初のテストに合格する、明らかに間違った実装を書いてみよう。次の関数は、入力のコピーを単に返すだけだ。

dstring toFront(dstring str, dchar letter) {
    dstring result;

    foreach (c; str) {
        result ~= c;
    }

    return result;
}

unittest {
    immutable str = "hello"d;

    assert(toFront(str, 'h') == "hello");
    assert(toFront(str, 'o') == "ohell");
    assert(toFront(str, 'l') == "llheo");
}

void main() {
}
D
unit_testing.solution.1

最初のテストはパスするが、2つ目のテストは失敗する:

./deneme
core.exception.AssertError@deneme.d(17): ユニットテストの失敗
Bash

すべてのテストに合格する正しい実装は以下の通りだ:

dstring toFront(dstring str, dchar letter) {
    dchar[] firstPart;
    dchar[] lastPart;

    foreach (c; str) {
        if (c == letter) {
            firstPart ~= c;

        } else {
            lastPart ~= c;
        }
    }

    return (firstPart ~ lastPart).idup;
}

unittest {
    immutable str = "hello"d;

    assert(toFront(str, 'h') == "hello");
    assert(toFront(str, 'o') == "ohell");
    assert(toFront(str, 'l') == "llheo");
}

void main() {
}
D
unit_testing.solution.2

テストが最終的に通過した:

./deneme
Bash

この関数は、テストに合格することが確実なので、さまざまな方法で変更することができる。次の2つの実装は、最初の実装とは大きく異なるが、テストによるとどちらも正しい。

  • std.algorithm.partitionを活用する実装:
    import std.algorithm;
    
    dstring toFront(dstring str, dchar letter) {
        dchar[] result = str.dup;
        partition!(c => c == letter, SwapStrategy.stable)(result);
    
        return result.idup;
    }
    
    unittest {
        immutable str = "hello"d;
    
        assert(toFront(str, 'h') == "hello");
        assert(toFront(str, 'o') == "ohell");
        assert(toFront(str, 'l') == "llheo");
    }
    
    void main() {
    }
    D
    unit_testing.solution.3

    注釈:上記のプログラムで登場する=>構文は、ラムダ関数を作成する。ラムダ関数については、後の章で説明する。

  • 次の実装では、まず、文字列内に特殊文字が何回出現するかをカウントする。次に、その情報をrepeated()という別の関数に送信して、結果の最初の部分を生成する。repeated()には、独自のユニットテストセットがあることに注意。
    dstring repeated(size_t count, dchar letter) {
        dstring result;
    
        foreach (i; 0..count) {
            result ~= letter;
        }
    
        return result;
    }
    
    unittest {
        assert(repeated(3, 'z') == "zzz");
        assert(repeated(10, 'é') == "éééééééééé");
    }
    
    dstring toFront(dstring str, dchar letter) {
        size_t specialLetterCount;
        dstring lastPart;
    
        foreach (c; str) {
            if (c == letter) {
                ++specialLetterCount;
    
            } else {
                lastPart ~= c;
            }
        }
    
        return repeated(specialLetterCount, letter) ~ lastPart;
    }
    
    unittest {
        immutable str = "hello"d;
    
        assert(toFront(str, 'h') == "hello");
        assert(toFront(str, 'o') == "ohell");
        assert(toFront(str, 'l') == "llheo");
    }
    
    void main() {
    }
    D
    unit_testing.solution.4