JUnit実践入門のMockitoの部分をJMockitでやってみた
下書きの状態で眠ってた記事を書きました。
タイトルの通りで、JUnit実践入門のMockitoについて説明してる部分をJMockitでやってみました。
すごく勉強になる本でした。読むと色々な知識がつながってすっきりです。
JMockitは、Mockitoと比べていいところは、staticメソッドだろうがコンストラクタだろうがfinalなクラスのメソッドだろうがモックオブジェクトで振る舞いを制御できちゃいます。
よくないところは、ソースがMockitoを使った場合の方が見やすいとこですかね。個人的にですが。
試したJMockitのバージョンは0.999.18です。
==========
2017/12/5 追記
バージョン1.37で試した記事を書きました
JUnit実践入門のMockitoの部分をJMockit (version 1.37) でやってみた - bati11 の 日記
==========
JMockitのチュートリアルが詳しく書かれているので、これを読むと色々分かるかもです。
The JMockit Testing Toolkit Tutorial
スタブメソッドの定義
JMockitには大きく分けて3つのAPIの分けられます。
- Expectations API
- Verifications API
- Mockups API
Expectations APIはスタブメソッドを定義するために、Verifications APIはスタブメソッドがどのように呼び出されたかを検証するために、Mockups APIはspyオブジェクトを生成するために、それぞれ使用することができます。
JMockitでは、メンバー変数に@Mockedアノテーションを付加してスタブオブジェクトを宣言できます。
下の例で@Mockedアノテーションを使っています。
そして、NonStrictExpectationsクラスをnewしてインスタンス初期化子で、スタブメソッドの定義を行います。
result = "Hello" というのがスタブメソッドの返り値を指定しているところです。resultという変数に代入した値が、直前のメソッド呼び出しの返り値になります。1行で書いてますが別に2行になっても構いません。
下の例だと、stub.get(0)という呼び出しで、"Hello"が返ってくるってことですね。
@Mocked List<String> stub; // スタブオブジェクトを宣言 @Test public void スタブメソッドの定義() { new NonStrictExpectations() {{ stub.get(0); result = "Hello"; // スタブメソッドの定義 }}; assertThat(stub.get(0), is("Hello")); // 検証 }
例外を送出するスタブメソッド
例外を送出させる場合は、resultに例外オブジェクトを代入します。
@Mocked List<String> stub; @Test(expected=IndexOutOfBoundsException.class) public void 例外を送出するスタブメソッド() { new NonStrictExpectations() {{ stub.get(0); result = "Hello"; stub.get(1); result = "World"; stub.get(2); result = new IndexOutOfBoundsException(); }}; stub.get(2); // 例外が送出される }
void型を返すスタブメソッド
voidの場合も変わらすresultに代入すれば例外が発生します。
@Mocked List<String> stub; @Test(expected=RuntimeException.class) public void メソッドの戻り値がvoidのスタブメソッド() { new NonStrictExpectations() {{ stub.clear(); result = new RuntimeException(); }}; stub.clear(); }
任意の引数に対するスタブメソッド
任意の引数としたい場合は、anyIntという変数を指定します。Mockitoと似てますね。
また、@Mockedアノテーションを付加したメンバー変数を消しました。代わりにテストメソッドの引数でスタブオブジェクトを指定しています。
JMockitでは、メソッドの引数にスタブオブジェクトを指定する方式も可能です。
@Test public void 任意の整数に対するスタブメソッド(final List<String> stub) { new NonStrictExpectations() {{ stub.get(anyInt); result = "Hello"; }}; assertThat(stub.get(0), is("Hello")); assertThat(stub.get(1), is("Hello")); assertThat(stub.get(999), is("Hello")); }
スタブメソッドの検証
さて、スタブメソッドが引数"Hello"で2回呼び出されて、引数"World"では1回も呼び出されてない、ということを検証したい場合ですが、JMockitではVerifications APIを使います。
下のように、Verificationsクラスをnewしてインスタンス初期化子に検証処理を書きます。
使ったことないですが、MockitoでいうところのatMostメソッドと同じことができそうな、maxTimesとかもチュートリアルを見るとあります。
@Test public void スタブメソッドの検証(final List<String> mock) { mock.clear(); mock.add("Hello"); mock.add("Hello"); new Verifications() {{ mock.clear(); times = 1; mock.add("Hello"); times = 2; mock.add("World"); times = 0; }}; }
また、Verifications APIを使わずに、Expectations APIのみでも同じ上と同じ検証ができます。
この場合、NonStrictExpectationsクラスではなく、Expectationsクラスを使ってスタブメソッドを定義します。
@Test public void スタブメソッドの検証_Exceptationsで指定(final List<String> mock) { new Expectations() {{ mock.clear(); times=1; mock.add("Hello"); times=2; mock.add("World"); times=0; }}; mock.clear(); mock.add("Hello"); mock.add("Hello"); }
部分的なモックオブジェクト
JMockitでもMockitoと同じように実オブジェクトを利用し、部分的なモックオブジェクトを作ることが可能です。
実オブジェクトを、Expectationsのコンストラクタ引数に指定します。
@Test public void 部分的なモックオブジェクト() { final List<String> mock = new ArrayList<String>(); new Expectations(mock) {{ mock.size(); result = 100; }}; mock.add("Hello"); assertThat(mock.get(0), is("Hello")); assertThat(mock.size(), is(100)); }
スパイオブジェクト
JMockitでスパイオブジェクトを作成するには、Mockups APIを使用します。
MockUpクラスをnewする時に型パラメータとして監視対象のクラスを指定します。そして、インスタンス初期化子で監視対象となるメソッドを宣言します。
メソッドの宣言は、監視対象のメソッドと同じ名前、同じ返り値の型、引数は第1引数にInvocation、第2引数以降は監視対象メソッドの引数、というようにします。
実オブジェクトでの本来の振る舞いをさせるには、invocationのproceedメソッドを呼び出します。
private static class SpyExample { Logger logger = LoggerFactory.getLogger(JMockitMockingTest.class); public void doSomething() { logger.info("doSomething"); } } @Test public void JMockitのMockUpAPIを使ったテスト() { // Setup SpyExample sut = new SpyExample(); final StringBuilder infoLog = new StringBuilder(); MockUp<Logger> spy = new MockUp<Logger>() { @Mock public void info(Invocation invocation, String message) { infoLog.append(message); invocation.proceed(); } }; sut.logger = spy.getMockInstance(); // Exercise sut.doSomething(); // Verify assertThat(infoLog.toString(), is("doSomething")); }
こんな感じでできました。
やっぱりMockitoの方がソース見やすいですね。
ただ、saticメソッドやコンストラクタの振舞いも制御できるので、レガシーアプリと戦う時とかに使えそうです。
JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
- 作者: 渡辺修司
- 出版社/メーカー: 技術評論社
- 発売日: 2012/11/21
- メディア: 単行本(ソフトカバー)
- 購入: 14人 クリック: 273回
- この商品を含むブログ (69件) を見る