SE情報技術研究会’s blog

http://se-info-tech.connpass.com

2016-01-10 『JUnit実践入門』読書会(第6章)の振り返り

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)

第6章 テストコンテキスト

  • テストコンテキストとはテストに関する内部状態や前提条件をあらわすもの
  • テストコードは可読性が高くなるようにメンテナンスすべき
  • しかし、プロダクションコードと同じ手法でメンテすると、逆に可読性が低くなる可能性がある

テストコードの整理

整理せず単純にテストケースを作っていくと同じコードがあふれテストケースも読みづらくなる

テストケースのグループ化

  • テストケースで共通の初期化処理を行うものでグループ化すべき
    → 実行前のデータの状態ごと
  • テストケースで検証するメソッド単位でのグループ化は初期化処理の共通化ができないのでやらないほうがよい
  • Enclosedテストランナーを利用することでテストケースをネストしたクラスでまとめることが可能
コメント
  • 実際にp.95であげられているテストケースをそれぞれテストメソッドに実際にしているか?
    → 原則は1テストケースでテスト対象の1アクション
    → 原則はわかっているが実際は簡単なものなど1テストケースにまとめてることもある
    → 原則に従って分けると毎回初期化処理がテストケースを開始するため時間がかかりすぎることもあるのでは?
    → UTの実行が終わるまで時間がかかると短いサイクルでまわせなくなるのでTDDが成り立たない

Enclosedによるテストクラスの構造化

  • org.junit.experimental.runners.Enclosed
  • 対象のテストクラスに@RunWith(Enclosed.class)をつける
  • 初期化処理が共通化しているテストケースをネストしたクラスのまとめることが可能
  • ネストしたクラスにさらにネストすることも可能だが3階層以上になる場合はテストの対象クラスの責務が大きすぎる可能性があるので再検討したほうがよい

著者おすすめの構造化

  • 外側のテストクラスのクラス名は「テスト対象クラス名+Test」
  • ネストするクラスの名前はグループ化するテストケースの前提条件にする ※ 日本語可
  • こうすることでeclipseのクイックアウトライン機能(Ctrl+o)を使うとテストクラスの概要が見やすくなる

例(本より)

@RunWith(Enclosed.class)
public class ItemStockTest {
  
  public static class 空の場合 {
    ItemStock sut;
    
    @Before
    public void setUp() throws Exception {
      sut = new ItemStock();
    }
    
    …テストケース略
  }
  
  public static class 商品Aを1件含む場合 {
    ItemStock sut;
    
    @Before
    public void setUp() throws Exception {
      sut = new ItemStock();
      sut.add("A", 1);
    }
    
    …テストケース略
  }
}

コンテキストのパターン

共通データに着目する

  • 入力値や期待値などのテストで、使用するデータに着目してグループ化するパターン
  • テストケースごとに前や後処理を記述するのではなく、同じデータのテストケースをネストしたクラスにまとめ、そこで前処理と後処理を行ったほうが効率的

共通の状態に着目する

  • テスト対象クラスが状態を持ち事前処理で特定の状態にしないといけない場合のグループ化
  • 対象の状態にするための共通の事前処理をネストしたクラスににまとめ、そこで前処理と後処理を行ったほうが効率的

コンストラクタのテストを分ける

  • インスタンス化テストは他のテストケースとわけてグループ化したほうがよい

テストクラスを横断する共通処理

  • テストクラスの構造化を行うことでテストクラス内の共通処理は整理できるが異なるテストクラス間では共通した処理をまとめられない
  • そのような場合でも継承は使うべきではない
  • 解決策としてユーティリティクラスなどに抽出する
  • 解決策としてルールを使う ※ 第9章にて詳細を説明
コメント
  • 対象の1クラスに対しテストクラスが複数あるのはありか?
    → 原則は1対1なのでは?
    → 1テストクラスにするとソースコードが長くなりメンテが大変になる場合もあるので分ける派もいる