社内「JUnit実践入門」読書会第一回まとめ #junitbook

社内で「JUnit実践入門」読書会を開催したのでそのまとめです。
読書会初回、私がこのような催しを開催するのも初だったのですが、5人も参加していただけました。

今回の対象範囲は本当に基本的な部分だったので、私がそれなりにまとめた資料を読み進めつつ参加者でツッコミを入れたり、議論を行うという形で進めました。
ツッコミや議論もそれなりに入りそれなりに盛り上がったので、初回としてはまぁまぁの出来ではないでしょうか?

尚、開催に至るまでの経緯はそのうちblogに書く予定。

開催日程とか

日時 1/12(土) 10:00〜12:00
場所 自社会議室
参加者 6名

対象範囲

第1章 JUnitチュートリアル
第2章 ユニットテスト
第3章 テスティングフレームワーク

第1章 JUnitチュートリアル

1.1 なぜ、ユニットテストを行うのか?
  • プログラミングを行うのは人間であり、そして人間は必ず間違いを犯す。
  • 間違いがあるのではないかという不安を安心に変えるため。
  • ユニットテストは作成したクラスやメソッドが期待したとおりに振る舞うかどうかを検証する。
  • ユニットテストを行えば全てOKではなく、あくまでもそのユニットのみのテストであり、他のモジュールとの結合テストなどは必要。

1.2 JUnitとは?
1.3 JUnitを始めよう

Quick JUnitのショートカット

  • Ctrl + 9 テスティングペアを開く
  • Ctrl + 0 JUnitを実行する
  • Ctrl + Shift + 0 JUnitをデバックモードで実行する
1.4 テストコードの記述
  • テストコードのルール
    • テストクラスはpublicクラスとする
    • テストメソッドは、org.junit.Testアノテーションを付与したpublicメソッドとする
    • テストメソッドは、戻り値がvoidであり、引数を持たない
    • throws句は自由に定義可能
  • テストメソッドは日本語で記述(テストコードはドキュメントとして読みやすいことが重要)
  • 値の検証はassertThatメソッドを使用する(「junit.Assert.assertThat」 static importするのが一般的)
    • 1つ目の引数は実測値(actual value
    • 2つ目の引数は期待値(expected value
    • expectedはファクトリメソッドであるisを使用して与える。
    • assertThatの引数の一番前にエラーメッセージも設定できる。(assertThat("Help! Integers don't work", 0, is(1));)
      が、あまり使わないほうが良い。失敗した時にはメソッド名でわかる用にすべき。
  • テスト失敗時にはビューに表示されるスタックトレースを元に調査を行う。
  • 例外が投げられることをテストするには@Testのexpected属性を使用する。「@Test(expected=IllegalArgumentException.class)」
    • 全ての例外のテストをルールのExpectedExceptionを使用するのもありか?(エラーメッセージなどの確認も可能)

第2章 ユニットテスト

2.1 ソフトウェアテストとは?
  • 検証する内容を定義し、ソフトウェアが期待通りに動作するかを確認する事。
  • テストの定義 : 「ある条件下においてソフトウェアの振る舞いを記録し、その記録が期待される結果となることを検証するプロセス」
  • 1つのテスト項目 = テストケース : 「どのような条件でどのような操作をしたならば、こうなることが期待される」
  • テストスイーツ : テストケースをまとめたもの
  • 目的 : テストによって目的が異なるため、目的を意識することが重要。
  • 「完璧なテスト」は不可能。どの程度で「十分に良いテスト」とするかを意識する。
2.2 テスト技法
  • ホワイトボックステスト : 内部ロジックや仕様を考慮してテストケースを作成。
  • ブラックボックステスト : 外部使用のみからテストケースを作成。
  • 同値クラスに対するテスト : 同様の結果をもたらす値を同値クラスとしてグループ化してその中からテストデータを選択する技法。
  • 境界値に対するテスト : 異なる結果をもたらす値の近傍からテストデータを選択する技法。
2.3 ユニットテストとは?
  • クラスやメソッドの検証テスト。
  • 期待された振る舞い = 対象のクラスやメソッド の仕様をテスト
  • プログラムとして実行できる仕様書。
  • 何度でも繰り返し実行可能、かつ頻繁に実行することも可能。
  • 目的はソフトウェアの品質を高めることではない
    • テストがない状態での導入時には品質向上と言っちゃても良い?w
  • 目的 : 仕様を保証、不安なく開発を進めることができる。
    すばやいフィードバックと仕様の明確化
  • プログラムとして実行できること = 自動化されたテストであること
2.4 ユニットテストのパターン
  • 自動化されたテスト(繰り返しいつでも実行できること)
  • 不安定なテスト(結果が一定でないテストを避けること)
  • ドキュメントとしてのテスト(仕様書として読めること)
  • 問題の局所化(テスト失敗時に問題を特定しやすいこと)
  • 不明瞭なテスト(可読性の低いテストコードは避けること)
  • 独立したテスト(実行順序に依存しないこと)

第3章 テスティングフレームワーク

3.1 テスティングフレームワークとは?
  • 「どのようにテストを記述して、実行し、検証するか」のしくみを提供。
3.2 JUnitによるユニットテストの記法
  • テストクラスはpublicクラスとする
  • テストメソッドは、org.junit.Testアノテーションを付与したpublicメソッドとする
  • テストメソッドは、戻り値がvoidであり、引数を持たない
  • 必要であればthrows句にExceptionを記述
  • 「test」 + ctrl + space でテストメソッドのひな形を挿入可能。
3.3 可読性の高いテストコードの書き方
  • テストケースは以下の3点が重要
    • テストを行う前提条件
    • テストに用いる入力値や操作
    • テストを行った時に期待する値や動作
  • テスト対象となるクラスやオブジェクトは「SUT」(System Under Test)と呼ぶ。
  • テスト対象クラスのインスタンスは「sut」という変数名で扱う。
  • テストクラスはテスト対象と同じパッケージ
  • テストクラス名は「テスト対象のクラス名 + Test」
  • テスト対象の処理後の返り値やオブジェクトの変化する状態などは実測値(actual value
  • 仕様として返すべき値や変化する状態である値は期待値(expected value
  • テストコードで使用する変数名も「actual」、「expected」とすると可読性が高まる。
  • テストし難いコードはたいてい良くないコード
  • テストを行いやすいメソッドとは以下の通り
    • メソッドが戻り値を持つ
    • メソッドの呼び出しの結果、副作用がない
    • 同じ状態、同じパラメータで実行すれば、必ず同じ結果を返す。
  • 乱数や現在時刻など実測値が変化する場合にはモックオブジェクトを使うことでテスト可能。
  • テストは4つのフェーズで実行される。(これらの区切りを記述しとくのは良い習慣)
    • 事前準備
    • 実行
    • 検証
    • 後処理

このように1行でかけてしまうテストケースも

assertThat(sut.hoge("a"), is(12));

しっかりと実測値と期待値を変数として定義した方が可読性が高い。

// 事前準備
expected = 12;
String arg = "a";

// 実行
actual = sut.hoge(arg);
  
// 検証
assertThat(actual, is(12));
  • テスト実行時に必要とされるデータや状態の準備のコードが多くなる場合には外部クラスや外部定義ファイルなどに記述しておくと可読性が高まる。
3.4 比較検証を行うアサーション
  • 値の検証はassertThatメソッドを使用する
    「org.junit.Assert.assertThat」(static importするのが一般的)
    1つ目の引数は実測値(actual value)2つ目の引数は期待値(expected value)を含むMatcherオブジェクトを指定。
  • isメソッドで生成されるMatcherオブジェクトは同値性(equals)の比較
3.5 JUnitが提供するアノテーション
  • @Test : テストメソッドを宣言する
    • expected属性 : 例外の確認
    • timeout属性 : テストのタイムアウト値を設定
  • @Ignore : テストの実行から除外
  • @Before : テストの実行前に処理を行う
    • メソッドの名はsetUp
    • publicかつvoid
  • @After : テスト実行後に処理を行う
    • メソッド名はtearDown
    • publicかつvoid
  • @BeforeClass : テストの実行前に一度だけ処理を行う
    • メソッド名はsetUpClass
    • publicかつstaticかつvoid
  • @AfterClass : テストの実行後に一度だけ処理を行う
    • メソッド名はtearDownClass
    • publicかつstaticかつvoid
  • 実行順序
  1. setUpClass
  2. setUp
  3. テストケース1
  4. testDown
  5. setUp
  6. テストケース2
  7. testDown
  8. tearDownClass
3.6 JUnitのテストパターン
  • 標準的な振る舞いを検証するテスト
    • 事前準備、実行、検証、後処理
  • 例外の送出を検証するテスト
    • @Testのexpected属性に投げられる例外クラスを指定する
    • 詳細に検証する場合にはExpectedExceptionを使用する。
  • コンストラクタを検証するテスト