« [コラム] Web サービスのユニットテスト | トップページ | [ブログ紹介] TDD アンチパターン »

2009年8月28日 (金)

[コラム] テストファーストの終了条件 ( どれだけテストを書けばいいのか? )

私はテストファーストの初心者に対しては、 全部のテストケースをユニットテストに書け、 と教えています。 しかし、 TDD 的には全部書かなくてもいいんです。 Uncle Bob の TDD 三原則の 2番目、 「失敗させるためにしか、 ユニットテストを書いてはならない。」 が示しています。 これは言い換えれば、

失敗するテストをもうそれ以上書けなくなったら、 終了

…ということです。

品質保証のためにメソッドのブラックボックステストを作るのとは、 違います。 テストファーストは、 製品コードよりも先にテストケースを書きますから、 ブラックボックステスト的です。 しかし、 テストケースを追加するときには、 既存の製品コードを見るわけですから、 そこはブラックボックステストではなくなります。

例として、 引数で渡した人の名前に対して挨拶を組み立ててくれる BuildHelloMessageFor() メソッドを作ることを考えてみます。
まず、 このメソッドのふるまいを全部網羅した表を考えます。 ( メソッドの外部設計 )

入力
string person
出力
返値
null string.Empty
string.Empty string.Empty
1文字以上の文字列 {foo} "Hello, {foo}!"

※ 「1文字以上の文字列」 といっても、 スペースだけの時はどうなる、 とかありますが、 これは例題ということで。
※ この 「ふるまいパターン表」 も、 TDD 的には書く必要はありません。 表に書き出してみたほうが把握しやすいので、 TDD に慣れるまではやってみると良いです。

この表をそのままテストケースとすれば、 ユニットテストを 3つ書くことになりますが、 実際はどうでしょうか?
まず、 3番目のテストを書いて実装してみましょう。

// テストコード
[TestMethod()]
public void BuildHelloMessageForTest_Case03()
{
    Assert.AreEqual<string>("Hello, Mr. Who!", MessageBuilder.BuildHelloMessageFor("Mr. Who"));
}

// 製品コード
public static string BuildHelloMessageFor(string person) {
    return string.Format(null, "Hello, {0}!", person);
}

次に 1番目のテストケースを書いてみましょうか。

// テストコード
[TestMethod()]
public void BuildHelloMessageForTest_Case01()
{
    Assert.AreEqual<string>("", MessageBuilder.BuildHelloMessageFor(null));
}

このテストはもちろん失敗します。 そこで、 次のように実装を追加したとすると…

// 製品コード
public static string BuildHelloMessageFor(string person) {
    if (person == null)
        return string.Empty;

    return string.Format(null, "Hello, {0}!", person);
}

…残った 2番目のテストケースは通らない ( と確信できる ) ので、 そのユニットテストも書くことになります。 この場合は、 全部のテストケースをテストコードとして表現することになります。

ところが、 1番目のテストケースに対して、 .NET Framework 2.0 から定石になった書き方で実装したとします。 つまり…

// 製品コード
p
ublic static string BuildHelloMessageFor(string person) {
    if(string.IsNullOrEmpty(person))
        return string.Empty;

    return string.Format(null, "Hello, {0}!", person);
}

…と、 String クラスの IsNullOrEmpty() メソッドを使ったとします。 この場合は、 2番目のテストケースも通る ( と確信できる ) ので、 もう失敗するユニットテストを書くことができません。 すなわち、 2番目のテストケースをテストコードとして書くことなく、 TDD 的にはこのメソッドは完成したと宣言できます。

このように、 完全なブラックボックステストと、 テストファーストは異なります。

なお、  「( と確信できる )」 と書きました。 そのような確信が持てないとき、 すなわち、 失敗するか成功するか、 どうなるかわからないテストケースはどうすべきでしょうか? やってみたら成功してしまうかもしれないけれども、 そのユニットテストは書くべきでしょう。
また、 TDD に慣れていない初心者のうちは、 TDD 三原則にこだわらず、 全部のテストケースを書いたほうが、 抜けが無くて良いと思います。

|

« [コラム] Web サービスのユニットテスト | トップページ | [ブログ紹介] TDD アンチパターン »

*コラム」カテゴリの記事

コメント

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: [コラム] テストファーストの終了条件 ( どれだけテストを書けばいいのか? ):

« [コラム] Web サービスのユニットテスト | トップページ | [ブログ紹介] TDD アンチパターン »