jMock
jMockとは
モックオブジェクトを利用してJavaコードのテストを行うためのライブラリ。
特徴
- すばやく簡単にモックオブジェクトを作成することが可能。
- オブジェクトの相互作用に対して柔軟な定義を行うことができる。
- 拡張も簡単
モックとスタブ
スタブとモックは本質的には同じ(擬似的なオブジェクト)だが、戦略(状態中心 or 相互作用中心)が異なる。また、モックオブジェクトはスタブを包括していると捉えることもできる。
わかりやすいドキュメント。
Martin FowlerによるMocks Aren’t Stubs(和訳)
モックオブジェクトを利用したテスト
階層分離設計では、隣り合っている層への依存関係が発生するため、切り離してテストを実行しなければならない。

図1:サンプルクラス図
上記の図ではサービス層とデータベースアクセス層が切り分けられているが、サービス層の単体テストを行うときに実際の実装クラスを利用することができない(実際のデータベースにアクセスしてしまうため。)
そこで、データベースアクセス層のクラスをモックオブジェクトに置き換えてテストを実行することで、層を切り分けたテストを実施することができる。

図1:サンプルクラス図
上記の図ではサービス層とデータベースアクセス層が切り分けられているが、サービス層の単体テストを行うときに実際の実装クラスを利用することができない(実際のデータベースにアクセスしてしまうため。)
そこで、データベースアクセス層のクラスをモックオブジェクトに置き換えてテストを実行することで、層を切り分けたテストを実施することができる。
モックオブジェクトのテストにjMockを利用する理由
あるクラスに対してテストを実行する場合、そのクラスから依存しているクラスのモックオブジェクトを用意することになる。
ユーザインタフェース層のテストを行う場合、サービス層のモックオブジェクトが必要になり、サービス層のテストを行う場合、上記のようにデータベースアクセス層のモックオブジェクトが必要となる。
要するに、ほとんどのクラスに対してモックオブジェクトを作成しなくてはいけないのだが、これを手動で作成するのは効率が悪すぎる。また、各モックに対してのメンテナンスの必要も発生してしまう。
jMockは動的にモックオブジェクトの生成を行う(動的Proxy)ので、開発者がモックオブジェクトを実装する手間をまるごと省くことができる。
ユーザインタフェース層のテストを行う場合、サービス層のモックオブジェクトが必要になり、サービス層のテストを行う場合、上記のようにデータベースアクセス層のモックオブジェクトが必要となる。
要するに、ほとんどのクラスに対してモックオブジェクトを作成しなくてはいけないのだが、これを手動で作成するのは効率が悪すぎる。また、各モックに対してのメンテナンスの必要も発生してしまう。
jMockは動的にモックオブジェクトの生成を行う(動的Proxy)ので、開発者がモックオブジェクトを実装する手間をまるごと省くことができる。
入手
オフィシャルサイトより。
サンプルコード(QuickStart)
上記サンプルクラス図を実装。
HumanDao.java
public interface HumanDao {
public void select();
public void update(Object dto);
}
HumanLogic.java
public class HumanLogic {
private HumanDao humanDao;
public HumanDao getHumanDao() {
return humanDao;
}
public void setHumanDao(HumanDao humanDao) {
this.humanDao = humanDao;
}
public void createHuman(){
humanDao.select();
// 省略
humanDao.update(new Object());
}
}
jMockを使った単体テストコード
HumanLogicTest.java
import org.jmock.Mock;
import org.jmock.MockObjectTestCase;
public class HumanLogicTest extends MockObjectTestCase {
public void testCreateHuman() throws Exception{
Mock mock = mock(HumanDao.class);
mock.expects(once()).method("select").withNoArguments();
mock.expects(once()).method("update").withAnyArguments();
HumanLogic humanLogic = new HumanLogic();
humanLogic.setHumanDao((HumanDao)mock.proxy());
humanLogic.createHuman();
}
}
テストメソッドの最初の行でHumanDaoのモックオブジェクトを作成している。
続いてモックオブジェクトに対して想定を設定している。サンプルの場合、HumanLogicクラスの中ではHumanDaoクラスのselectとupdateが1回づつ実行されているので、モックオブジェクトに対して想定として設定している。
mock.expectsの引数InvocationMatcher
| メソッド | 意味 |
|---|---|
| once() | 一回実行 |
| never() | 複数 |
メソッドに渡す引数の設定
| メソッド | 意味 |
|---|---|
| with() | 任意の引数を設定。 |
| withNoArguments() | 引数をとらない。 |
| withAnyArguments() | 引数は何でもよい。 |
上記コードではmock()メソッドを利用してモックオブジェクトを生成している。この場合、テストコードの最後で内部的に想定のテストが実行される。
これとは別の方法で、newを利用してモックオブジェクトを生成することもできる。
Mock mock = new Mock(HumanDao.class);
この方法でモックオブジェクトを生成した場合、コード内で明示的にverifyメソッド呼び出しを記述する必要がある。
mock.verify();
リンク
- オフィシャル
- Mocks Aren’t Stubs
- モックとスタブ(上記和訳)

