JUnit 実践入門 体系的に学ぶユニットテストの技法 - 第8章 パラメータ化テスト テストケースとテストデータの分離
第8章 パラメータ化テスト テストケースとテストデータの分離
- 異なる入力値について似たようなテストを実施する場合など、テストケース毎の差異が入力値と期待値だけ、のような状況下において、入力値と期待値をパラメータとして外部化することで見通しを良くする
8.1 テストデータの選択
- 同値クラス
- 組み合わせによるテストデータ選択
- 「プログラムに混入する不具合のほとんどは、単項目に関連する不具合」
- よって「条件をすべて満たす、あるいは1つだけ満たさない」でテストデータを選択すると幸せ
- すべて満たす → true返すはずが、条件1つバグってるの検知できる
- 1つだけ満たさない → false返すはずが、条件1つバグってるの検知できる
- 境界値も組み合わせるとよりイイね
8.2 入力値と期待値のパラメータ化
- テストデータの組み合わせによりテストコードが冗長化するのを防ぐ、パラメータ化テスト parameterized test
- テストデータとテストメソッドを分割する
Theories - パラメータ化テストのテストランナー
- @RunWith でテストクラスに付与する
@Theory - テストメソッドに指定するアノテーション
- @Test の代わり
- 任意の引数を宣言できる
@DataPoint - パラメータを指定するアノテーション
- パラメータは static かつ public なフイールドで定義する
package ch08; import org.junit.experimental.theories.DataPoint; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; @RunWith(Theories.class) public class ParameterizedTest { @DataPoint public static int INT_PARAM_1 = 3; @DataPoint public static int INT_PARAM_2 = 4; public ParameterizedTest() { System.out.println("初期化"); } @Theory public void 引数を持つテストメソッド(int param) throws Exception { System.out.println("引数を持つテストメソッド (" + param + ")"); } }
初期化
引数を持つテストメソッド (3)
初期化
引数を持つテストメソッド (4)
- @DataPoint 指定した値が渡さている
- 「初期化」が二回出てきてる → コンストラクタが二回呼ばれてる
- 都度都度オブジェクトが生成されている
package ch08; import org.junit.experimental.runners.Enclosed; import org.junit.experimental.theories.DataPoint; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; @RunWith(Enclosed.class) public class EnclosedParameterizedTypeTest { @RunWith(Theories.class) public static class intのパラメータ化テスト { @DataPoint public static int INT_PARAM_1 = 3; @DataPoint public static int INT_PARAM_2 = 4; @Theory public void 引数がint型のテストメソッド(int param) throws Exception{ System.out.println("引数がint型のテストメソッド (" + param + ")"); } } @RunWith(Theories.class) public static class Stringのパラメータ化テスト{ @DataPoint public static String STRING_PARAM_1 = "Hello"; @DataPoint public static String STRING_PARAM_2 = "World!"; @Theory public void 引数がString型のテストメソッド(String param) throws Exception { System.out.println("引数がString型のテストメソッド (" + "param" + ")"); } } }
引数がString型のテストメソッド (param)
引数がString型のテストメソッド (param)
引数がint型のテストメソッド (3)
引数がint型のテストメソッド (4)
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (65件) を見る
JUnit 実践入門 体系的に学ぶユニットテストの技法 - 第7章 テストフィクスチャ テストデータや前提条件のセットアップ
第7章 テストフィクスチャ テストデータや前提条件のセットアップ
7.1 テストフィクチャとは
- テストで扱う、データやテスト実行環境、オブジェクトの状態など
ユニットテストのテストフィスクチャ
- テスト対象オブジェクト
- 入力値
- 期待値
- テスト実行までに必要な、テストオブジェクトの操作
- ファイルなど外部リソース
- データベースやソケットサーバなど、外部システム
依存クラスや依存外部システムのモックオブジェクト
一般にテスト実行フェーズよりも、セットアップのコードのほうが長くなる
フレッシュフィクスチャ
- フィクスチャはテストケースごとに独立し、テスト実行ごとに初期化され、終了時に開放する → フレッシュフィクスチャ戦略
フィクスチャとスローテスト問題
- フィクスチャのセットアップに長い時間がかかること → スローテスト問題 slow tests
- DB のテーブルデータ全レコード削除とかやってるから
- テストの並列実行、共有フィクスチャ、カテゴリ化テストなどの解決策
共有フィクスチャ
- 共有フィクスチャは、スローテスト問題 shared fixture の解法の一つ
- テストケースごとの独立性が弱くなる問題
- テストコードのメンテナンス性低下。あるフィクスチャを参照しているテストケースは、全部でどれだけ??
7.2 フィクスチャのセットアップパターン
インラインセットアップ
- テストメソッドごとにフィクスチャのセットアップを行う
- コードでの見通しが良くなるが、長すぎると可読性は下がる
- 20 行がひとつの目安
暗黙的セットアップ
- Before アノテーションが付与されたメソッド(セットアップメソッド)のこと
- テストメソッド実行前に暗黙的 implicit に実行されるからね
- 各初期化処理の共通部分
- Enclosed テストランナーと相性良し
- 構造化!共通前処理!インナークラス!
生成メソッドでのセットアップ
- テストクラスをまたいで暗黙的セットアップは出来ない…
- 共通した初期化処理を独立したクラスのメソッドに抽出する 生成メソッドパターン
- staticインポートできるように、独立クラスにstaticで書くと幸せ。日本語メソッド名で内容を表示してあげるともっといいね
外部リソースからのセットアップ
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (65件) を見る
JUnit 実践入門 体系的に学ぶユニットテストの技法 - 第6章 テストのコンテキスト テストケースの構造化
第6章 テストのコンテキスト テストケースの構造化
6.2 テストケースの整理
- メソッド単位で整理して個別クラス作成するよりも、初期化処理で整理するほうが、一般に良い
- 前者は初期化処理を共通化できないため
6.3 コンテキストのパターン
- 共通のデータに着目する
- たいてい、同一のデータに対して、削除なり挿入なりしている
- 共通の状態に着目する
- ArrayList のテスト時を想定。テスト前にある状態をセットアップしているはず
- コンストラクタのテストを分ける
- テスト対象オブジェクトの生成のテスト。初期状態を検証する、特別なコンテキスト。
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (65件) を見る
JUnit 実践入門 体系的に学ぶユニットテストの技法 - 第5章 テストランナー テスト実行方法の制御
第5章 テストランナー テスト実行方法の制御
5.1 コマンドラインからの JUnit の実行
5.2 テストランナーとは
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (65件) を見る
明解 Javaによるアルゴリズムとデータ構造 6-9_度数ソート
6-9 度数ソート
- 分布数え上げソートとも呼ばれる
- 要素の大小関係を比較しない
- 安定
度数ソート
学生9人の10点満点テストを例に
- 度数分布表の作成 - 各点数の学生が何人いるか調べる
- 累積度数分布表の作成 - 0点からその点数までに何人の学生がいるか調べる
- 目的配列の作成
- 配列のコピー
- 作者: 柴田望洋
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2007/11/07
- メディア: 単行本
- 購入: 5人 クリック: 42回
- この商品を含むブログ (19件) を見る
JUnit 実践入門 体系的に学ぶユニットテストの技法 - 第4章 アサーション 値を比較検証するしくみ
第4章 アサーション 値を比較検証するしくみ
4.1 Assert による値の比較検証
- org.junit.Asset にアサーションメソッドが多数定義されている
- static インポートされることを想定した static メソッド
- assertThat と fail 2つ知っていれば十分。
assertThat - 汎用的な値の比較検証
- JUnit では、比較のルールと比較検証のしくみを分離している
- ルール: Matcher オブジェクト。場合によってカスタマイズも出来る
- しくみ: assertThat メソッド。仕組み自体はそのまま使いまわしていける。
fail - テストを失敗させる
4.2 Matcher API によるアサーションの特徴
- Matcher API は Hamcrest という、JUnit の独立した拡張ライブラリの一つだったが、統合された
- 以前は assertEquals メソッドに比較ルールを実装していたが、柔軟性や可読性に欠けた
- 可読性の高い記述
- 柔軟な比較
- 詳細な情報の提供
4.3 Matcher API の利用
- org.hamcrestCoreMatchers クラスや、org.junit.matchers.JUnitMatchers クラスなどに定義された、static なファクトリメソッドから Matchers オブジェクトを生成する。
- static インポートはワイルドカード指定が便利
CoreMatchers が提供する Matcher
- is
- 独自実装クラスのオブジェクトに対して利用する場合は、適切な equals メソッドがオーバーライドされているか要確認。基本はデフォルト equals が使われるため、予期しない結果になるかも。
- nullValue
- not
- notNullValue
- sameInstance
- instanceOf
- 実測値が null の場合は必ず失敗
JUnitMatchers が提供する Matcher
- hasItem
- hasItems
カスタム Matcher の作成
- 日付をざっくり比較する(ミリ秒とかいらない) Matcher をつくる
手順
isDate クラスの作成
- BaseMtcher クラスを extends
ファクトリメソッド作成
- static インポートにより、自然言語に近い形で表記したいため
- プロジェクトで複数のカスタム Matcher 作るなら、ファクトリメソッドをまとめて定義したクラスを作成すると吉
コンストラクタの定義
matches メソッドの実装
- 実測値を引数に取り、一致なら true、そうでなければ false を返す
- 一致しない場合には、describeTo メソッドが呼び出される
discribeTo メソッドの実装
- フレームワークに比較検証が失敗した理由を通知する
テスト失敗メッセージの確認
- ここを見て原因がすぐに分かるよう、describeTo を工夫する
ソースコード
プロダクションコード
package ch04; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; public class IsDate extends BaseMatcher{ private final int yyyy; private int mm; private int dd; Object actual; IsDate(int yyyy, int mm, int dd) { this.yyyy = yyyy; this.mm = mm; this.dd = dd; } @Override public boolean matches(Object actual) { this.actual = actual; if (!(actual instanceof Date)) return false; Calendar cal = Calendar.getInstance(); cal.setTime((Date) actual); if (yyyy != cal.get(Calendar.YEAR)) return false; if (mm != cal.get(Calendar.MONTH) + 1) return false; if (yyyy != cal.get(Calendar.DATE)) return false; return true; } @Override public void describeTo(Description desc) { desc.appendValue(String.format("%d/%02d/%02d", yyyy, mm, dd)); if (actual != null) { desc.appendText(" but actual is )"); desc.appendValue(new SimpleDateFormat("yyyy/MM/dd").format((Date) actual)); } } public static Matcher dateOf(int yyyy, int mm, int dd) { return new IsDate(yyyy, mm, dd); } }
テストコード
package ch04; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static ch04.IsDate.*; import java.util.Date; import org.junit.Test; public class IsDateTest { @Test public void 日付の比較() { assertThat(new Date(), is(dateOf(2012, 1, 12))); } }
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (65件) を見る
明解 Javaによるアルゴリズムとデータ構造 6-8_ヒープソート
6-8 ヒープソート
ヒープ
- 親の値が子の値以上である、完全2分木のこと
- 兄弟での大小関係は任意
- ヒープのことを別名、半順序木 partial ordered tree ともいう
- a[i] に対して、
- 親は a[(i - 1) / 2]
- 左の子は a[i * 2 + 1] ※剰余切り捨て
- 右の子は a[i * 2 + 2]
根を削除したヒープの再構築
- 根を取り出す
- 最後の要素(最下流の最も右側に位置する要素)を根に移動する
- 自分より大きい方の子と交換して一つ下流に降りる作業を、根から始めて、以下の条件何れかが成立するまで繰り返す
- 子の方が値が小さい
- 葉に到達した
ヒープソートへの拡張
- 変数 i の値を n - 1 で初期化する
- a[0] と a[i] を交換する
- a[0], a[1], …, a[i - 1] をヒープ化する
- i の値をデクリメントして 0 になれば終了
そうでなければ2に戻る
配列のヒープ化
ソースコード
import java.util.Scanner; class HeapSort { static void swap(int[] a, int idx1, int idx2) { int t = a[idx1]; a[idx1] = a[idx2]; a[idx2] = t; } static void downHeap(int[] a, int left, int right) { int temp = a[left]; int child; int parent; for (parent = left; parent < (right + 1) / 2; parent = child) { int cl = parent * 2 + 1; int cr = cl + 1; child = (cr <= right && a[cr] > a[cl]) ? cr : cl; if (temp >= a[child]) { break; } a[parent] = a[child]; } a[parent] = temp; } static void heapSort(int[] a, int n) { for (int i = (n - 1) / 2; i >= 0; i--) { downHeap(a, i, n - 1); } for (int i = n - 1; i > 0; i--) { swap(a, 0, i); downHeap(a, 0, i - 1); } } public static void main(String[] args) { Scanner stdIn = new Scanner(System.in); System.out.println("ヒープソート"); System.out.print("要素数: "); int nx = stdIn.nextInt(); int[] x = new int[nx]; for (int i = 0; i < nx; i++) { System.out.print("x[" + i + "]: "); x[i] = stdIn.nextInt(); } heapSort(x, nx); System.out.println("昇順にソートしました。"); for (int i = 0; i < nx; i++) { System.out.println("x[" + i + "]: " + x[i]); } } }
ちょい飽きたので飛ばし
- 作者: 柴田望洋
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2007/11/07
- メディア: 単行本
- 購入: 5人 クリック: 42回
- この商品を含むブログ (19件) を見る