読者です 読者をやめる 読者になる 読者になる

EasyMockを使ってみた

ユニットテストを行うときに困るのが
HttpServletRequest等のサーバなどで生成されるインスタンス
引数としているメソッドのテストです。


そんなときはモックという擬似オブジェクトを
利用したテストを行うのが一般的です。


私もJUnitでテストを行う為にモックを
利用しようと思ったのですが、
昔利用していたmockobjectというライブラリが無くなっていました。(年を感じます;_;)


今はEasyMockやjMockといったライブラリが利用されているようです。
ということで今回、EasyMockを初めて使ってみました。以下、使い方です。
http://www.easymock.org/index.html
(バージョンは2.4を使用)


まず最初にダウンロードしたら
easymock.jarをクラスパスに通します。


次に、

import static org.easymock.EasyMock.*


をテストソースにインポート宣言します。
以上でテスト準備終了です。


それでは、HttpServletRequestのスタブを作成して
テストを行ってみます。

//HttpServletRequestのモックを生成
HttpServletRequest mock = createMock(HttpServletRequest.class);

// モックオブジェクトに振る舞いを記憶
expect(mock.getParameter("hogehoge")).andStubReturn("fugafuga");

// モックオブジェクトを再生モードに切り替え
replay(mock);

// テスト実施
assertEquals("結果", Hoge.getResult(mock));


EasyMockの挙動は少し変わっていて、以下のような流れになります。


モック生成 -> 処理の記録 -> 再生モードに切り替え -> 再生


と、いきなりMockの利用は出来ず、必ず再生モードに切り替える必要があります。


JMockの場合は、わざわざ再生モードに切り替える必要はなく、
処理の記録には文字列でメソッドを指定します。


私は、癖はありますが、Eclipse等のIDEの補完機能が利用できる
EasyMockのアプローチは結構ありだと思います。


上記は、単純なスタブとしての利用でしたが今度は
モックとしてオブジェクトのメソッドが正しく呼び出されたかの
確認も行ってみます。

//HttpServletRequestのモックを生成
HttpServletRequest mock = createMock(HttpServletRequest.class);

// モックオブジェクトに振る舞いを記憶
expect(mock.getParameter("hogehoge")).andReturn("fugafuga");

// モックオブジェクトを再生モードに切り替え
replay(mock);

// テスト実施
assertEquals("結果", Hoge.getResult(mock));

// モックオブジェクトが振る舞い通りに呼ばれたか確認
verify(mock); 


andStubReturnをandReturnに変更し、最後にverifyを追加しました。
これでHoge.getResult内でmockのgetParameterが呼ばれたかを確認して
呼ばれなかった場合はverifyでエラーとなります。


上記は処理内部でgetParameterを1回呼び出すテストになりますが、
複数呼び出す指定や曖昧な呼び出しなども表現できます。

振る舞いの記憶いろいろ

1回呼び出し
//返り値なし
mock.setAttribute("foo", "bar");
//返り値あり
expect(mock.getParameter("hogehoge")).andReturn("fugafuga");
3回呼び出し
//返り値なし
mock.setAttribute("foo", "bar");
expectLastCall().times(3);
//返り値あり
expect(mock.getParameter("hogehoge")).andReturn("fugafuga").times(3);
少なくとも1回呼び出し
expect(mock.getParameter("hogehoge")).andReturn("fugafuga").atLeastOnce();
任意の回数呼出し
expect(mock.getParameter("hogehoge")).andReturn("fugafuga").anyTimes();
//以下と同じ?
expect(mock.getParameter("hogehoge")).andStubReturn("fugafuga");
Exceptionをスロー
expect(mock.getParameter("hogehoge")).andThrow(new Exception());
引数のチェック
//booleanであればOK
expect(mock.getSession(anyBoolean())).andReturn(null);

//指定されたクラスであればOK
expect(mock.getParameter(isA(String.class))).andReturn("fugafuga");

//配列のチェック
String[] strings = new String[]{"foo", "bar"};
mock.setArray(aryEq(strings));
メソッド呼び出し順序のテスト
//メソッドの呼び出し順序をチェックするモックを生成
HttpServletRequest mock = createStrictMock(HttpServletRequest.class);
モックの再利用
//モックのリセット(記憶モードに戻る)
reset(mock);


このようなツールを利用して
少しでもテストの自動化を行うことで
品質の維持が出来るのではないかなと思います。