JUnit実践入門 3日目(ユニットテストについて)

Junitなどテストをしてんするフレームワークは、テスティングフレームワーク呼ばれる。
「どのようにテストを記述して、実行し、検証するか」の仕組みを提供する。
→一定のフォーマットでテストケースを記述できるのでテストコードが読みやすくなる。

コードの種類
ソフトウェア本体のコード :プロダクションコード
テストのためのソースコード:テストコード
テストコードを書くときに重視すべき点
プロダクションコードと同じように重複をなるべく減らすように整理をしたものより、可読性の高さを求められる
→テストコードを読むことで、テストで何を前提として、何を行い、何を期待しているのか理解しやすいようにする。

テストケースで明確にするもの
・前提条件
・入力値や操作
・テスト実行後の期待する値や動作
これらが不完全な場合、テスト自体が不完全なものとなる。

SUT(Sysytem Under Test)
テスト対象となるクラスやオブジェクトをSUTと呼ぶ。
一つのテストケースでは一つのSUTのみ対象とする(複数のSUTを一つのテストケースで扱った場合、そのテストケースがなんのテストをしているのかが不明瞭となる)
テストコードでは、テスト対象クラスのインスタンスを生成し、「sut」という名前の変数で扱うことでテスト対象がどれかわかりやすくなる。
※慣習としてテストクラスはテスト対象と同じパッケージとし、クラス名を「テスト対象のクラス名+Test」とする。このためテスト対象のクラス(SUT)は命名規則から特定される。

@Test
public void isValidはuserNameとpasswordが空でないときにtrueを返す() throws Exception{
 UserForm sut = new UserForm("user01", "1234");
 assertThat(sut.isValid(), is(true));
}

実測値と期待値
実測値:メソッドが返す値やオブジェクトの変化する状態
期待値:仕様として返すべき値や変化する状態である値
→可読性を高めるために、テストコードに含まれる変数名は実測値に「actual」、実測値に「expected」とするとよい。

@Test
public void userNameが空のときエラーメッセージを返す() throws Exception{
 UserForm sut = new UserForm("", "1234");
 String expected = "ユーザーIDは必須項目です";
 String actual = sut.getErrorMessage();
 // actual:実測値 expected:期待値
 assertThat(actual, is(expected));
}

※一つのテストメソッドに複数の実測値や期待値が必要となった場合、テスト対象が大きすぎる可能性がある。
 複数の検証要素が存在する場合は、メソッドを増やすことで検証している要素がブレないようにする。

メソッドと副作用
わかりにくいテストコードとなる最大の要因は、テスト対象がテストしにくい設計となっていること。
テストの可動性やメンテナンス性を高めるために効果的なことは、ユニットテストを行いやすいクラス設計とすること。
ユニットテストが行いやすいメソッドの性質
・メソッドが返り値を持つ
→実行結果が明確でわかりやすい
・メソッド呼び出しの際、副作用がない(オブジェクトの内部状態が変更されないこと)
→繰り返しテストを実行しても、都度同じ条件でテスト実行ができる
 →副作用があるメソッド:ArrayListのaddとか
・同じ状態、同じパラメータを実行すれば、必ず同じ結果を返す
→乱数による影響がない

これらのような性質を持つメソッドを関数的と呼ぶ

4フェーズテスト
ソフトウェアのテストは次の4フェーズで実行される
・事前準備
→テスト対象オブジェクト(SUT)の初期化、必要な入力値、期待される結果などの準備を行う
・実行
→SUTに対し、テストする操作を一つだけ実施する
・検証
→テストの結果として得られた実測値が期待値と等価であるかを比較検証する
・後処理
→次のテストに影響がないように後始末をする。

これら4フェーズを意識して書かれたテストコードは可読性が高くなる。
このためコメントとして描くフェーズの区切りを記述しておくことが良い。

@Test
public void メソッド名()throw Exception{
 //Setup
 ArrayList<String> sut = new ArrayList<String>();
 sut.add("Hello");
 sut.add("World");

 //Exercise
 sut.remove(0);

 //Verify
 assertThat(sut.size(), is(1));
 assertThat(sut.get(0), is("World"));
}

Eclipceのテスト作成時のテンプレートにコメントを追加して自動的に記述されている状態にするのが良さそう。