JUnitで試験するときに使うMockライブラリ
JUnitを使用した単体試験のときに使えるMockについて。(なんか疲れたからまとめっぷりは中途)
以下みたいなのがある。
- EasyMock
- インスタンスのMockがほしいときに使うとよい
- ネストしたクラスをMock化したいときはセッターとかDIとか使う
- Mockito
- PowerMock
個人的にDJunitはもうオワコンなので書かない。(カバレッジバグったりするし)
使うなら「EasyMock + PowerMock」か「Mockito + PowerMock」なのかな。
Mockito VS EasyMock
上記はMockito側の人だけど、適当にぐぐってみてもMockito押しの人が多い気がするなぁ。。。
ぐーぐる先生のトレンドによると2011年ちょい前にMockitoが逆転しておる。
http://www.google.co.jp/trends/explore#q=EasyMock%2C%20Mockito&cmpt=q
EasyMock
EasyMockは下記のライフサイクルで使用する。- Mock化対象クラス・インターフェースからMockオブジェクトを作成(createMock())
- Mockオブジェクトのメソッドの振る舞いを記録(expect())
- Mockオブジェクトを記録モードから再生モードへ(replay())
- Mockオブジェクトを使ったテスト
- Mockオブジェクト再生後の検証(verify())
- Mockオブジェクトの初期化(reset())
■サンプルで使用するインターフェース(Mock化する対象)
public interface Concater { /** * 文字列を結合する */ String concat(String str1, String str2) throws Exception; }
■サンプルのテストコード
@Test public void testMock() throws Exception { // 1. Mock化対象クラス・インターフェースからMockオブジェクトを作成 Concater mock = EasyMock.createMock(Concater.class); // 2. Mockオブジェクトのメソッドの振る舞いを記録(Mock作成時は記録モード) EasyMock.expect(mock.concat("FirstName", "SecondName")).andReturn("FirstName SecondName"); // 3. Mockオブジェクトを記録モードから再生モードへ EasyMock.replay(mock); // 4. Mockオブジェクトを使ったテスト assertThat(mock.concat("FirstName", "SecondName"), is("FirstName SecondName")); // 5. Mockオブジェクト再生後の検証 EasyMock.verify(mock); // 6. Mockオブジェクトの初期化 EasyMock.reset(mock); }
本来はインスタンス化しづらいオブジェクトをMock化するんだけど、ここでは説明用って事で意味の無いコードになってる。
Mock化対象クラス・インターフェースからMockオブジェクトを作成
Mockを作成する際に使用するメソッドには以下の種類がある。
EasyMock.verify()した際にチェックされる内容が変わる。
引数にはMock化したクラス・インターフェースのクラス型を渡す。
- 通常モード EasyMock.createMock()
- 厳密モード EasyMock.createStrictMock()
- 緩和モード EasyMock.createNiceMock()
Mockオブジェクトのメソッドの振る舞いを記録
メソッド呼び出しの際の引数
Mockのメソッドを呼ぶ出す際の引数として明示的な値ではなく、なんらかのオブジェクト(EasyMock.anyObject())のようにフンワリさせることができる。
EasyMock.expect(mock.concat(
(String) EasyMock.anyObject(),
(String) EasyMock.anyObject())).andReturn(1.5);
任意のオブジェクトとはいえnullはヤダってときはEasyMock.notNull()を使う。
EasyMock.expect(mock.concat(
(String) EasyMock.notNull(),
(String) EasyMock.notNull())).andReturn("FirstName SecondName");
正規表現(EasyMock.matches())を指定することもできる。
EasyMock.expect(mock.concat( (String) EasyMock.matches("[A-Z][A-Z][A-Z]"), (String) EasyMock.matches("[A-Z][A-Z][A-Z]"))).andReturn("FirstName SecondName");
EasyMock.matches()の代わりにEasyMock.find()を使うと、指定の文字列を含む任意のStringを受け付けることができる。
他にも以下のようなメソッドが用意されている。
- EasyMock.anyInt()
- EasyMock.anyShort()
- EasyMock.anyByte()
- EasyMock.anyLong()
- EasyMock.anyFloat()
- EasyMock.anyDouble()
- EasyMock.anyBoolean()
- EasyMock.lt()
- EasyMock.gt()
- EasyMock.eq()
複数モックを使用する場合
IMocksControlによって複数のモックオブジェクトを生成することが可能になり、複数のモックにまたがってメソッド呼出しの順序を チェックすることができる。
例えば、IMyInterfaceインタフェースに対して2つのモックオブジェクトをセットアップ するとしましょう。そしてまず、mock1.a()とmock2.a()を順番に、次にmock1.c() とmock2.c()を任意の回数、最後にmock2.b() と mock1.b()を順に実行することを 期待します。この設定を行うコードは以下のようになります。
IMocksControl ctrl = createStrictControl(); IMyInterface mock1 = ctrl.createMock(IMyInterface.class); IMyInterface mock2 = ctrl.createMock(IMyInterface.class); mock1.a(); mock2.a(); ctrl.checkOrder(false); mock1.c(); expectLastCall().anyTimes(); mock2.c(); expectLastCall().anyTimes(); ctrl.checkOrder(true); mock2.b(); mock1.b(); ctrl.replay(); ctrl.verity(); ctrl.reset();
EasyMock(org.easymock.EasyMock)の他にEasyMockClassExtension(とorg.easymock.classextension.EasyMock)がある。
後者は本来インタフェースからしかモックを作れないEasyMockの機能を拡張し、クラスからでもモックを作れるようにしてくれる機能みたい。
参考:
http://easymock.org/
http://www.ibm.com/developerworks/jp/java/library/j-easymock.html
http://www.geocities.jp/nn_51/easymock/EasyMock2_2_Documentation.html
http://prepro.wordpress.com/tag/easymock/
Mockito
■サンプルのテストコード@Test public void testMock() throws Exception { // 1. Mock化対象クラス・インターフェースからMockオブジェクトを作成 Concater mock = Mockito.mock(Concater.class); // 2. Mockオブジェクトのメソッドの振る舞いを記録 Mockito.when(mock.concat("FirstName", "SecondName")).thenReturn("FirstName SecondName"); // 3. Mockオブジェクトを使ったテスト assertThat(mock.concat("FirstName", "SecondName"), is("FirstName SecondName")); // 4. Mockオブジェクト再生後の検証、および初期化 EasyMock.verify(mock).clear(); }
EasyMockよりコード量が少ない。
参考:
Mockit Web Site: http://code.google.com/p/mockito/
Mockit Documents: http://docs.mockito.googlecode.com/hg/latest/org.mockito/Mockito.html
http://qiita.com/mstssk/items/98e597c13f12746c907d
PowerMock with EasyMock
■Mavenを使う場合<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> <version>1.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <version>3.2</version> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.5.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-easymock</artifactId> <version>1.5.2</version> <scope>test</scope> </dependency> </dependencies>
■使い方
import org.junit.Test; import org.junit.runner.RunWith; import org.easymock.EasyMock; import org.powermock.api.easymock.PowerMock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) //PowerMockの利用を宣言 @PrepareForTest({MockTarget1.class, MockTarget2.class}) //モック対象のクラスを宣言(複数可能) public class TestClass { @Test public void testMock() throws Exception { // テストを書く } }
staticメソッドのMock
ここでのPowerMockのフルネームは「org.powermock.api.easymock.PowerMock」。
// 1. PowerMockでMock化したいstaticメソッド持つクラスをロード PowerMock.mockStatic(<staticメソッドを呼び出すクラス>); // 2. EasyMockでstaticメソッドの振る舞いを記録 EasyMock.expect(<staticメソッド呼出>).andReturn(<戻り値>); // 3. PowerMockでMockを再生モードへ PowerMock.replay(<staticメソッドを呼び出すクラス>); // 4. Mockを使ったテスト // 省略 // 5. PowerMockでMock再生後の検証 PowerMock.verify(<staticメソッドを呼び出すクラス>); // 6. PowerMockでMockを初期化 PowerMock.reset(<staticメソッドを呼び出すクラス>);
クラスのstaticメソッドの部分的にMock化したい場合は
PowerMock.createPartialMock(<staticメソッドを呼び出すクラス>, <メソッド名1>, <メソッド名2>,・・・)
verify時のモードの違いにより
PowerMock.createStrictMock(Class<T> type) PowerMock.mockStaticNice(Class<?> type)
などがある。
コンストラクタを呼び出す場合は下記。
PowerMock.expectNew(<コンストラクタを監視したいクラス>, <引数1>, <引数2>,・・・).andReturn(<返すインスタンス>);
privateメソッドをMock化したい場合は下記。
instance = PowerMock.createPartialMock(<モックにするクラス>, new String[]{<メソッド名1>, <メソッド名2>,・・・}); PowerMock.expectPrivate(<インスタンス>, <メソッド名>, <引数1>, <引数2>,...).andReturn(<戻り値>);
詳しくは「http://powermock.googlecode.com/svn/docs/powermock-1.5.2/apidocs/index.html」。
参考:
https://code.google.com/p/powermock/
http://d.hatena.ne.jp/irof/20130517/p1
http://blog.tarotaro.org/archives/772