JUnitハンズオン勉強会
∼第一回 基本編∼
勉強会開始! でもその前に・・・
勉強会のゴール
受講者個々人が単体テストの重要性を理解し、
業務システムに適用可能な単体テストスキルを身につけることをゴールとします。
今後の計画
第1回 ∼基本編∼
JUnitに今後触れて行くに当たっての基本
第2回 ∼テストダブル(モック)編∼
JMockitを利用したモックテクニックをハンズオン
第3回 ∼DB編∼
DBUnitを利用したDB周りのテスト実施をハンズオン
第4回 ∼応用編∼
CIとの連携やDIコンテナ環境での単体テスト
利用するJUnitのバージョン
4.11を利用する前提とします。
目次∼基本編∼
① テストクラスの定義方法
② テストメソッドの作成と命名ルール
(こう書くと分かりやすい)
③ 単純なテストクラスの作成
(モダンなAssertion方法を学ぶ)
④ Exceptionの発生をテストする
⑤ 構造化テスト(@Enclosed)を活用しよう!
⑥ 主要なJUnit関連アノテーションと使いどころ
<パッケージ構成> <テストクラス定義>
・ソースフォルダとパッケージ
testソースフォルダのテスト対象クラスと同一パッケージに作成。
・クラス名
テストクラスの末尾に「Test」を付ける。
・親クラス
特に継承不要(JUnit3ではTestCaseを継承していたがJUnit4では不要)。
① テストクラスの作成ルール
・なぜソースフォルダを分けるのか?
リリース対象のコードとテストコードの混合を避けるため。
※ テストコードを除いてパッケージングしたい
・なぜ同一パッケージとするのか?
「どのクラスをテストしているかのわかりやすさ」と
「テスト対象クラスと同一の可視性でクラスが扱えるから」
(テスト対象クラスもImportなしで使えますよね!)
・なぜクラス名の末尾は「Test」なのか?
「わかりやすさ」と「探しやすさ」
※ 例えばリソース検索時に、対象クラスとテストクラスが同一窓に表示される!
ソースフォルダ、パッケージ名のなぜ?
・メソッドシグネチャ
@Testアノテーションを付与し、public、非static
引数なし(※1)、戻り値なし、Throws句は自由(※2)とします。
※1 組み合わせテストを実施する場合は引数を定義する場合があります。
これは第4回ハンズオンの場で解説予定です。
※2 Throws句の使いどころは後述します。
・メソッド名
日本語で何をテストしているのか分かるように書きましょう。
※JUnit4からメソッド名は「test」始まりでなくとも良くなりました。
② テストメソッドの作成と命名ルール
テストメソッド作成時の注意点等々
① テストの実行順に依存したメソッド定義をしないこと

JUnitでは同一クラス内のテストの実行順は不定です。

(テストは上から順番に実行される訳ではありません)

実行順が保証されていない以上、順序に依存するテストコードを書いては
いけません。

② テスト観点が不足していないか確認すること

実装寄りの視点で見ると

・メソッドの振る舞いに関するテスト

・Exceptionが発生する事を検証するテスト

・コンストラクタのテスト

に大別されます。また、個々のメソッド固有の観点も踏まえて

ケースに不足が無いか確認してください。
ハンズオン1 - テストクラスとメソッドの定義
これまでの説明を元に「Nabeatu」クラスにテストメソッドを定義してみましょう。
・テスト対象メソッドは「aho」
(引数が3の倍数、または 3 が含まれる場合は AHO になります)
・3を渡した時に AHO が返ります
・4を渡した時は4がそのまま返ります
※ 一旦メソッドの定義だけ行ってください。中の実装はこの後解説します。
フェーズ 内容 どこに書くか?
事前準備 (setup)
テスト対象オブジェクト(SUT ※)の初期化
※ System Under Testの略
テストメソッド内、@Before、
@BeforeClass
実行(exercise) テスト対象操作の実行(1度だけ実行) テストメソッド内
検証 (verify) テスト結果の実測値が期待値と等価か検証 テストメソッド内
後処理 (tear down) 次のテスト実行に影響が無いよう後処理する
テストメソッド内、@After
@AfterClass
単体テストは基本的に次の4フェーズで構成されます。
可読性向上のためにどのフェーズを実行しているかコメントを残しましょう!
4フェーズテストについて
JUnit4(正確には4.4以降)では、テスト結果は、org.junit.AssertクラスのassertThat
メソッドとMatcherと呼ばれる仕組みを利用して検証します。
③ Assertionの実施方法
Assert.assertThat(T actual, Matcher<? super T> matcher)
actual
テストの実測値(actual)を指定します。特に型を気にする必要はありません。
matcher
actualとの比較方法と期待値を保持するオブジェクトです。
主にorg.hamcrest.CoreMatchersクラスのメソッドを利用して作成します。
<例>
assertThat( hoge , is( hoge )); ・・・実測値と期待値をequalsで比較検証
assertThat(null, is(nullValue()); ・・・実測値がnullであることを検証
なぜMatcherを利用するのか?
・より自然言語(英語)に近い形で記述できる
【JUnit3】 assertEqual (expected, actual)
【JUnit4∼】 assertThat (actual, is(expected))
JUnit4の方が「actual is expected」というような自然言語に近い読み方ができます。
・オブジェクト検証のバリエーションが広がる
Junit3までは検証方法は予めJUnit3側で用意されたメソッドの種類しか存在しませ
んでした。(AssertEqual、AssertTure等)
Matcherを自作(第四回ハンズオンで解説)することで、
必要に応じて検証方法を追加することができます。
これにより、複雑なテスト検証処理をMatcherに移すことができます。
Junit4でも従来のAssertion(assertEqualsやassertTureなど)も引き続き利用
可能な状態となっています。ただし、これら旧来の検証メソッドは下位互換性のため
に残されているだけのようです。

原則として、今後はasserThatとMatcherの利用をお勧めします。
Matcherの種類と利用例
他にも沢山のMatcherが存在します。
参考サイト:http://qiita.com/opengl-8080/items/e57dab6e1fa5940850a3
※リファレンスとしてとても有用
Matcher 何を検証するか 利用例(検証成功の例)
is() 等価(equals)である assertThat( hoge , is( hoge ));
nullValue() nullである assertThat(null, is(nullValue());
startsWith() 指定した文字で始まる assertThat( hoge , is(startsWith( h ));
endsWith() 指定した文字で終わる assertThat( fuga , is(endsWith( a ));
greaterThan() 指定値よりも大きい assertThat(10, is(greaterThan(5));
lessThan() 指定値よりも小さい assertThat(5, is(lessThan(10));
not() 他matcher結果を反転(否定)する
assertThat( hoge , is(not( fuga ));
assertThat( hoge , is(not(nullValue()));
ハンズオン2 - 検証ロジックの実装
これまでの説明を元にハンズオン1で作成したメソッドを完成させてみましょう。
(入力値3と4を検証するんでしたね)
また、以下の観点で、新たにテストメソッドを追加、実装してみてください。
【追加するテスト観点】
・テストする数値の桁数の観点(1桁の場合だけでいいんでしょうか?)
・3の倍数の観点
・3の倍数ではないが、 3 を含む数値の観点
@Testアノテーションの引数「expected」に発生する想定のテストクラスを指
定することでException発生を検証できます。
※ try ~ catchを書いた検証ロジック(JUnit3)は不要となりました。
④ Exceptionの発生をテストする
この例の場合、ArithmeticExceptionが発生した場合はテスト成功となり、
逆に発生しない場合はテスト失敗となります。
※ 詳細なExceptionの検証(causeやmessageの取得/検証)は後述します。
JUnitはテストクラスのthrows句設定について特に制約はありません。
(定義してもいいですし、しなくても構いません)
throws句をつけた場合、実際に例外が送出されると(@Testのexpected引数を
指定していない場合は)テストは失敗扱いとなります。
テストメソッドでのthrows句利用
対象メソッドの
検査例外の有無
throws句要否 補足
有
要
※一律「throws Exception」
一律Exceptionを指定しておき、Exception発生を検証
するケースだけ、expected引数を利用すればOK。
無 否
<テストメソッドでのthrows句の利用ポリシー>
テスト対象メソッドが検査例外を発生させる場合は一律throws句を定義し、
Exception検証を行う場合にのみ@Testのexpected引数を利用する。
ハンズオン3 - 例外検証メソッドの追加
Nabeatuクラスのahoメソッドに以下の変更を加えてみましょう。
【Nabeatu#ahoの変更内容】
0未満(マイナス値)の数値で呼び出された場合は、IllegalArgumentExceptionを
throwする。
正しく例外が発生する事を検証するテストメソッドを追加してください。
※ 境界値も意識してテストメソッドを作成してみてください。
テストクラスは巨大になりがちです。
メソッド数が多くなりすぎ、コードの可読性が落ちる傾向にあります。
(ほとんどの場面でテスト対象のクラスよりも行数が多くなります)
構造化テストを利用することで、可読性を大幅に向上することができます。
⑤ 構造化テストの活用
<構造化テストとは>
同様の特徴を持つテストメソッドをグルーピングして整理することです。
JUnit4ではクラスをネストして定義し、関連するメソッドをまとめることでこれを実現
します。
<実装方法> ※サンプルは以降のページ参照
外側のクラスにEnclosedメソッドランナー(Enclosed)を指定し、staticなインナーク
ラス内でグルーピングを実施します。
<構造化テストを採用することのメリット>
・同じ特徴を持つメソッド群をグルーピングすることで可読性が大幅に向上します。
・初期化処理をグループ単位で共通化することで初期化ロジックの共通化できます。
構造化テストの構成例
テストメソッドA-1
一番外側のクラス(テストクラス)
ネストしたインナークラスA
(テストメソッドをグルーピング)
テストメソッドA-2
<構造化しない例> <構造化した例>
グループA用の初期処理
テストメソッドB-1
ネストしたインナークラスB
テストメソッドB-2
グループB用の初期処理
・
・
・
テストクラス
テストメソッドA-1
(A用の初期処理内包)
・
・
・
テストメソッドA-2
(A用の初期処理内包)
テストメソッドB-1
(B用の初期処理内包)
テストメソッドB-1
(B用の初期処理内包)
構造化テストの実装イメージ
ハンズオン4 - 構造化クラスの作成
Nabeatuクラスに以下の機能を追加しましょう。
【変更内容】
現在のahoメソッドは3の倍数と3を含む数値の場合に AHO を返しますが、任意の
数値を指定可能となるようクラスを修正してください。
どの数値を対象とするかはコンストラクタで指定可能としてください。
変更後のNabeatuプログラムを構造化クラスを使ってテストしてみましょう。
⑥ 主要なJUnitアノテーションと使いどころ
アノテーション 利用例や使いどころ
@Test
テストメソッドであることをJUnitに明示
@Test(expected=HogeException.class) … Exception発生を検証
@Test(timeout=1000) … 実行に1,000ms以上要した場合はテスト失敗扱い
@Before
各テストメソッドの実行前に呼び出されるメソッドを指定
・テストクラス(SUT)のインスタンス化 (SUTはテストクラスのフィールドとして保持)
・DBの初期化(第三回ハンズオンで解説)
など
@After
各テストメソッドの実行後に呼び出されるメソッドを指定
(DB関連の後処理等で利用。@Beforeと比べると利用頻度は低い)
@Ignore
無視するテストケースに指定。
テストメソッドだけ定義して、後から実装を作る、といった場合に有効。
@Rule
従来のテストでは実現できない様々な振る舞いをテストに対して追加する仕組み
ExpectedExceptionルールを利用する事で、@Test(expected)では不足していたExceptionの検証
(MessageやCauseの検証)を実施できる。
ハンズオン5 - 様々なバリエーションのテスト
以下のケースのテストメソッドを作成してみましょう。
※ 出典:書籍「JUnit実践入門」(一部)
① 副作用を持つメソッドのテスト

Counterクラスのincrementメソッドは呼び出す毎に1ずつ加算された値を返し
ます。このメソッドのテストクラスを作成してみましょう。

なお、初回のincrementメソッド呼び出し時は 1 を返します。

② 複合条件/テストしやすいメソッド設計を要するテスト

FizzBuzzを実行するクラスを作成し、単体テストを作成してみましょう。

引数として整数を渡し、0∼引数までのFizzBuzz結果をそれぞれ標準出力
(System.out)したいです。

どのようなメソッドの設計とすると単体テストしやすいでしょうか?
JUnitでよくある質問と回答
• テストはpublic/protected/package/privateメソッドのどこまでやるべきか?



基本的に外部からアクセス可能なメソッド(public/protected/package)はテストを
実施すべきです。

privateを除く可視性のメソッドは(packageは議論のあるところですが)振る舞いを外
部に公開する意味合いを持っていますので、テストで動作を検証する必要があります。



ただし、privateメソッドへのテスト実施は内容次第です。public∼packageのテストの
範疇で十分なテスト可能であれば問題ありませんが、業務処理等、複雑な処理をprivate
に切り出している場合は単独の方がテストを実施しやすい事もあります。
JUnitでよくある質問と回答
• 複数のテストケース(Assert)を1メソッドにまとめても良いか?



テスト成功/失敗した際に、 何が起こったのか 切り分けられる粒度とすることが大切で
す。

例えば、前出の「Nabeatu#aho」のテストを考えた時に、入力値1,2,3の検証を1つのメ
ソッドで実行したとしましょう。このメソッドが失敗した場合



・本来 AHO となるところが数値だったのか

・本来数値となるところが AHO だったのか



ぱっと見で切り分けできません。

テストコードの可読性を上げるためにも、成功/失敗時に 何が起こったのか を推測でき
るメソッドの切り分けにしましょう。
JUnitでよくある質問と回答
• 設計としてあり得ない値もテストケースとして含むべきか?

(例えば事前に引数nullチェックを実施する前提のメソッドにnullテストを行うか?)



メソッドの使われ方や、前提の置き方で要否を判断しましょう。

packageやprivateの可視性を持つメソッドでは、このメソッドの利用者が自身(または
自チームのメンバー)に限られるため、テストの必要性はさほど高くありません。

(要は利用者が内部に居るので、その前提が共有され遵守されていれば良いわけです)



ただし、publicやprotectedの可視性を持つメソッドは利用者が不特定多数となる場合
があるため、様々なテストを実施する必要があります。



どんなInputに対してどんなOutputを返すのか?はテストというよりもメソッドの設計
の問題です。利用場面を想定してメソッドの設計を行い、その設計通りの振る舞いとなっ
ているか単体テストでチェックしましょう。
基本編 完了
テストダブル編へ続く

※ すみません、次はもっと文字数減らします・・

第1回JUnit勉強会ハンズオン