実践JUnit
xUnitTestPatternsから学ぶユニットテスト


   2012.06.16 OSC 2012 北海道
   Shuji Watanabe (@shuji_w6e)




                                 1
本日の内容
本日の内容

    ユニットテストの目的
本日の内容

    ユニットテストの目的

ユニットテストの実践
本日の内容

    ユニットテストの目的

ユニットテストの実践

    ユニットテストの問題
SHUJI Watanabe
Java デベロッパー

 要件定義からテストまで全領域対応

Blog「やさしいデスマーチ」

札幌Javaコミュニティ

Java, Groovy, JavaFX, MongoDB

TDD, ユースケース駆動開発, テスト
WEB+DB PRESS vol.69
JUnit実践入門

 カスタムMatcher

 パラメータ化テスト

 テストの構造化

 カスタムRule

 Cucumebr-junit
XUnit Test Patterns
 http://xunitpatterns.com/

 Webで読めます(英語)
ユニットテストの目的
テストが
                                      必要なワケ




http://www.flickr.com/photos/essecento/4297955162/
スキル不足
仕様変更


                                     経験不足
複雑な要件

                                                   不安
   http://www.flickr.com/photos/yopse/3772030400/
不完全な人間




   http://www.flickr.com/photos/sharynmorrow/3948100/
ユニットテストとは?
ソフトウェアを構成する最小部品のテスト

主にクラスやメソッドが対象

対象が期待される振る舞いをするかを検証
テストの4象限モデル
                ビジネス面
                                   手動


            機能テスト      受け入れテスト




                                   製品を批評
 チームを支援




          ストーリーテスト    ユーザビリティテスト




                        負荷テスト
          ユニットテスト     パフォーマンステスト



 自動
                    技術面
テストの4象限モデル
                ビジネス面
                                   手動


            機能テスト      受け入れテスト




                                   製品を批評
 チームを支援




          ストーリーテスト    ユーザビリティテスト




                        負荷テスト
          ユニットテスト     パフォーマンステスト



 自動
                    技術面
テストの4象限モデル
                ビジネス面
                                   手動


            機能テスト      受け入れテスト




                                   製品を批評
 チームを支援




          ストーリーテスト    ユーザビリティテスト




                        負荷テスト
          ユニットテスト     パフォーマンステスト



 自動
                    技術面
チームを支援するテスト
自分のコードへの自信

積極的なリファクタリング

安心できるリリース
技術面のテスト
プログラマが行う

プロダクションコードの作成を支援する

ビジネス的価値 < 技術的負債の低減
ユニットテストの特徴
プログラマが行い、

開発チームを支援し、

技術面に属し、

自動化が可能。
セーフティネット




   http://www.flickr.com/photos/32010000@N08/2987901256/
品質とユニットテスト
ユニットテストで直接の品質はあがらない

品質を高めるにはビジネス面のテストが必要

要件を満たしているか?(受け入れテスト)

使いやすいか?(ユーザビリティテスト)

ユニットテストにより技術的負債が減る

変化や追加要求に強くなる
ユニットテストの実践
テストケースとは?
テスト対象(sut: System Under Test)に
特定の操作を行い、
実測値(Actual Value)が
期待値(Expected Value)となることを
検証(Verify)する
テストデータのサンプリング
「完璧なテスト」はできない

少ない入力値で効率良くテストを行う

様々なテスト技法

境界値分析

同値クラス

ペア構成テスト
テストケースの条件
再現性がある

曖昧さがない

自動化されたテスト(Automated Test)
4フェイズ テスト
初期化処理(SetUp)

実行(Exercise)    @Test
                public void test() {
検証(Verify)        // SetUp
                  // Exercise
後処理(TearDown)     // Verify
                  // TearDown
                }
アサーション
 アサーションとは「こうなる」という表明
assertThat(actual, is(expected));

  static importされたメソッド

  自然言語風の記述
  assert that actual is expected
  変数名(actual, expected)
Matcher API
 JUnit4の機能

 一致する条件を定義したオブジェクト
assertThat(actual, is(expected));

  isはIsオブジェクトを返す

  Isはequalsを使った一致を行うMatcher

  他にも多くのMatcherがある
リファクタリング
ユニットテストの主目的はリファクタリング

技術的負債を蓄積させない

安心してプロダクションコードを修正できる

内部実装に依存するテストは避けること
ユニットテストと設計
ユニットテストはサンプルコード

自分が最初のユーザー(Eat your own dog food)

APIが使いやすいかをテストする

テストできないAPIは再設計する
例)計算機クラス
public class Calculator {

    // 割り算を行うメソッド
    public int divide(int x, int y) {
      return x / y;
    }
}
例)テストコード
@Test
public void test() {
  // SetUp
  Calculator sut = new Calculator();
  float expected = 1.5;
  // Exercise
  float actual = sut.divide(6, 4);
  // Verify
  assertThat(actual, is(expected));
}
例)APIの設計ミス
public class Calculator {

    // 割り算を行うメソッド
    public float divide(float x, float y) {
      return x / y;
    }
}
API設計とユニットテスト
テストコードでAPI設計を検証出来る

 期待値が検証出来ないAPI

 使いにくい/解りにくいAPI

テストコードを意識してAPIを設計する

突き詰めるとテスト駆動開発(TDD)

 テストを先に書く(テストファースト)
TDDのサイクル
                            1.設計する
5.リファクタリング



               Heuristics




                               2.テストを書く



 4.テストを成功させる

                        3.コードを書く
ドキュメントとしてのテスト
テストコードは動作するドキュメント

具体的なサンプルコード

具体的な入力値と期待値

テスト成功による保証

JavaDocや仕様書より正確なドキュメント
ユニットテストの問題
不安定なテスト
テスト結果が不定

ランダム性やシステム時間に依存したテスト

環境に依存したテスト

テスト結果が一定でないと信頼できない

オオカミ少年問題

常にすべてのテストが成功することを維持
脆いテスト
少しの変更で多くのテストに影響する

疎結合になっていない

データの影響範囲が大きい

インターフェイス(API)の設計が甘い

いずれもテストのメンテナンスコストを増大
増えるテストコード
重複

DRY原則(Don t Repeat Yourself)

やり過ぎると可読性が落ちる

テストはテストケースで独立すべき

ネストしたクラスの利用(Enclosed)

パラメータ化テスト
Enclosedテストクラス
 テストクラスの構造化

 テストをコンテキスト(文脈)で整理
@RunWiht(Enclosed.class)
public void ArrayListTest {
  public static class 初期状態のとき {
    @Test public void test() {}
  }
  public static class 要素が1つのとき {
    @Test public void test() {}
  }
}
Enclosedテストクラス
 テストクラスの構造化

 テストをコンテキスト(文脈)で整理
@RunWiht(Enclosed.class)
public void ArrayListTest {
  public static class 初期状態のとき {
    @Test public void test() {}
  }
  public static class 要素が1つのとき {
    @Test public void test() {}
  }
}
パラメータ化テスト
 テストデータとテストメソッドを分離

@RunWiht(Theories.class)
public void CalculatorTest {
  @DataPoints public static Param[] PARAMS = {
        new Param(3, 4, 12), new Param(1, 5, 5), ....
  };
  @Theory public void divideのテスト(Param p) {
     //
  }
}
パラメータ化テスト
 テストデータとテストメソッドを分離

@RunWiht(Theories.class)
public void CalculatorTest {
  @DataPoints public static Param[] PARAMS = {
        new Param(3, 4, 12), new Param(1, 5, 5), ....
  };
  @Theory public void divideのテスト(Param p) {
     //
  }
}
詳しくはWEB+DB vol.69で
JUnit実践入門

 カスタムMatcher

 パラメータ化テスト

 テストの構造化

 カスタムRule

 Cucumebr-junit
テストしにくいオブジェクト
システム時間や外部API

モックやスタブの活用

多用は禁物

内部実装に依存しないように注意して使う

mockito, EasyMock
まとめ
ユニットテストを学ぼう
目的は開発の支援

基本となる「型」を習得(4フェイズテスト)

テスト技法を学び効率良くサンプリング

パターンを学ぶ

API設計とテストは密な関係(TDD)

テスティングフレームワークを活用する
ユニットテストを超えて...
継続的インテグレーション(テスト)

テストによる実行網羅率(カバレッジ)

テスト駆動開発

自動化された受け入れテスト

振る舞い駆動開発

探索的テスト
おしまい

JUnit実践入門 xUnitTestPatternsで学ぶユニットテスト