2012年8月5日日曜日

現在時刻に依存する振る舞いのテスト・コーディング

現在時刻が 17時台から 22時台までなら夕方(evening)とする」。

そんなメソッドを、Eclipse と JMockit を使ったテストファーストで書いてみる(こういった実行時に依存する条件を含むコードは、昔はテスト・コーディングが難しかったけど、ツールが進歩した最近はそうでもない)。

====

対象メソッドが CurrentTimeHogehoge クラスの isEvening() メソッドだとすると、テストクラスはこんな感じになるはず

public class CurrentTimeHogehogeTest {
  @Test public void isEvening() {
    assertTrue(CurrentTimeHogehoge.isEvening());
  }
}
この時点では CurrentTimeHogehoge クラスがまだ存在しないから、Eclipse のエディタ上で赤い下線が引かれている。その行で Ctrl+1 を押してクラス生成を選ぶとダイアログが開くから、ソースフォルダを適当に直して Finish。

エディタに戻ると、今度は isEvening() に赤い下線が移っているので、これも Ctrl+1 で 生成する(いちいちタイピングしない)。

コンパイルが通ったところで、こんなコードになっているはず。

public class CurrentTimeHogehoge {
  public static boolean isEvening() {
    // TODO Auto-generated method stub
    return false;
  }
}
試しにテスト実行してみると失敗して赤くなるので、"return false" を "return true" に書き換えて、一旦グリーンにしておく。

続いて isEvening() の実装方針だけど、現在時刻を Calendar の get で取るって事で当たりを付ける。そうすると、テストコードは以下のような感じになる。

public class CurrentTimeHogehogeTest
  @Mocked("get") final Calendar cal = null;
  @Test public void isEvening() {
    new Expectations() {{
      Calendar mock = Calendar.getInstance(); 
      mock.get(Calendar.HOUR_OF_DAY); result = 17;
    }};
    assertTrue(CurrentTimeHogehoge.isEvening());
  }
}
cal を宣言する一行は、意味的にはフィールドの宣言と言うより、Calendar クラスの get メソッドを差し替えるという宣言になる。差し替える振る舞いは Expectations の中に記述していて、ここでは固定値17を返している。

ここでテスト実行してみて、になることをひとまず確認。呼ばれるはずのメソッド、Calendar#get() が 呼ばれなかったと、トレースされているはず。

本体コードの isEvening() は以下のように書き換える。とりあえず時間範囲の下側だけ判別するようにしている。

  public static boolean isEvening() {
    Calendar cal = Calendar.getInstance();
    int hour = cal.get(Calendar.HOUR_OF_DAY);
    return 17 <= hour;
  }
実行するとテストがグリーンになる。

続いて、境界値である、16→偽, 17→真, 22→真, 23→偽 についてテスト・コーディングするわけだけど、コードが重複するのが明らかなので、あらかじめ重複部分を以下のようなメソッドとして切り出しておく。

  private void validateIsEvening (final int hour, final boolean result) {
    new Expectations() {{
      Calendar mock = Calendar.getInstance(); 
      mock.get(Calendar.HOUR_OF_DAY); result = hour;
    }};
    assertEquals(result, CurrentTimeHogehoge.isEvening());
  }
これを、パラメータを変えてテストメソッドから呼び出す。以下のようになる。
@Test public void isEvening() {
    validateIsEvening(16, false);
    validateIsEvening(17, true);
    validateIsEvening(22, true);
    validateIsEvening(23, false);
  }

実行するとテストがになるから、isEvening の最終行を "return 17 <= hour && hour <= 22"に直す。これでテストが成功する。

====

演習だから、テストコードと本体コードの切り替えがかなり小刻みだけど、実戦だったらもうちょいざっくりした感じになると思う

0 件のコメント:

コメントを投稿