テストクラスの可読性向上

テストメソッド内で、フィクスチャのセットアップが長くて複雑になると、テストクラスが読みづらくなります。

テストクラスの可読性を向上させるためには、フィクスチャのセットアップが鍵になるので、セットアップ方法をまとめました。

テストフィクスチャとは

テストフィクスチャもしくはフィクスチャとはテストで扱うデータやオブジェクトの状態、テスト実行環境などを指します。

ユニットテストのフィクスチャには以下の要素が含まれます。

  • テスト対象オブジェクト
  • 入力値
  • 期待値
  • テスト対象オブジェクトが依存するオブジェクトの操作(状態)
  • ファイルなどの外部リソース
  • データベースなどの外部システム
  • モックオブジェクト

セットアップパターン

フィクスチャをセットアップする方法は複数あり、テスト対象に合わせて適切なセットアップパターンを選択することが可読性の向上につながります。

インラインセットアップ

テストメソッド内でフィクスチャのセットアップを行います。

テストメソッド内でテストコードが完結するので、テストコードの見通しが良くなります。

セットアップが長くて複雑な場合はテストメソッドが長くなり、可読性が下がります。

暗黙的セットアップ

セットアップメソッド(@Before)でセットアップを行います。

セットアップメソッドは各テストメソッドが実行される前に暗黙的に実行されます。

テストメソッドはテストの実行と検証が中心になるため、何をテストするかが明確になります。

テストメソッドごとにセットアップメソッドを定義することはできませんが、Enclosedテストランナーを使用すれば、テストクラスを複数のクラスに構造化し、クラスごとにセットアップメソッドを定義できます。

生成メソッドでのセットアップ

各テストメソッド(テストクラス)で共通した初期化処理をメソッドに抽出してセットアップを行います。

外部リソースでのセットアップ

xmlyamlにテストデータを設定し、ライブラリに読み込ませることでセットアップを行います。

長くて複雑なセットアップコードを消すことができますが、テストクラスとテストデータが分離されてしまいます。

外部リソースを使う場合はテストクラスからなるべく近いところに置くべきだと思います。

Groovyでのセットアップ

Groovyを使えば宣言的なコードでセットアップを行えます。

パラメータ化テスト

パラメータのバリエーションテストには、@Theoryと@DataPointを使ったパラメータ化テストが有効です。

セットアップパターンの使い分け

短くて単純なメソッドのテスト

インラインセットアップが有効です。

長くて複雑なメソッドのテスト

暗黙的セットアップが有効です。

生成メソッドと外部リソースでのセットアップはテストコードとテストデータが離れてしまうのがネックで、Groovyはプロジェクトによって使える使えないがあると思います。

セットアップのバリエーションが複数ある場合はEnclosedテストランナーと暗黙的セットアップの組み合わせが効果的です。

Enclosedテストランナーのメリット

  • テストクラスを構造化できる
  • クラスごとに暗黙的セットアップを定義できる

フィクスチャをクラス変数に保持する

プロダクションコードでは不用意にクラス変数を使うべきではないですが、テストコードではテストデータをクラス変数にするのはありだと思います。

以下の例では処理が単純過ぎるので効果はありませんが、実際の処理やテストデータは複雑になるので、テストデータをクラス変数に保持するとテストの実行と検証が楽になります。

public class SampleBeanTest {

  private SampleBean sampleBean;

  @Before
  public void setUp() throws Exception {
    sampleBean = new SampleBean("test");
  }

  @Test
  public void test() {
    SampleBean2 actual = sampleMethod(sampleBean);
    assertThat(actual.getStr2(), is(sampleBean.getStr()));
  }

  public SampleBean2 sampleMethod(SampleBean sampleBean) {
    return new SampleBean2(sampleBean.getStr());
  }

  public class SampleBean {
    private String str;
    public SampleBean(String str) {
      this.str = str;
    }
    public String getStr() {
      return str;
    }
  }

  public class SampleBean2 {
    private String str2;
    public SampleBean2(String str2) {
      this.str2 = str2;
    }
    public String getStr2() {
      return str2;
    }
  }

}

ただし、クラス変数でテストコードを制御するのは可読性が下がるのでやめたほうがいいです。(クラス変数によってアサートを切り替えるとか)

参考
JUnit実践入門 ── 体系的に学ぶユニットテストの技法:書籍案内|技術評論社
第6章 テストのコンテキスト
第7章 テストフィクスチャ
第8章 パラメータ化テスト