Mockソリューションを試す。 EasyMock


ユニットテストをやってると、クラスの依存関係によってテストクラスの作成が難しいことがよくあります。
今の現場は特にユニットテストを作成する事が多いので、この障害にぶち当たることが頻繁です。

こういったケースではモックオブジェクトを作成したりするのですが、Javaではモックオブジェクトを作るためのライブラリーがいろいろあるようです。
ざっと見た限り、jMock, rMock, djUnit(Virtual Mock) EasyMock等が上げられます。

最終的にはこれらの違いを考察したいのですが、今回はEasyMockを試してみます。

まずEasyMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
 <dependency>
   <groupId>org.easymock</groupId>
   <artifactId>easymock</artifactId>
   <version>2.4</version>
 </dependency> 

テスト対象のクラスは以下のようなものを用意しました。
コンストラクタでIServiceクラスをインターフェースとするクラスを受けとります。
このクラスのメソッドを実行するだけのメソッドexecute()、getName()があります。
 public class MockSample {
     private IService service;
     public MockSample(IService service) {
         this.service = service;
     }
     public void execute() {
         service.echo();
     }
     public String getName() {
         return service.get();
     }
 }
 interface IService {
     public void echo();
     public String get();
 }
モックオブジェクトの使用法としては、まずモックオブジェクトを作成する処理を記述します。
createMock(IService.class);のようにしてモックオブジェクトを作成します。この部分はGenericsを使っているため、キャストもなくすっきりしていますね。

次に、生成したモックオブジェクトを何らかの方法でテスト対象のクラスに注入して、適切に呼ばれているかをテストしていきます。
想定される呼び出しを記述し、replay(mock)〜verify(mock)の間で想定通りに呼び出されているかをテストしていきます。
また、モックオブジェクトが呼び出された時の戻り値も指定することができます。
 package test.easymock;

 import static org.easymock.EasyMock.*;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;

 public class TestEasyMockSample {
     private IService mock;
     private MockSample sample;
     @BeforeMethod
     public void setupMock() {
         // IServiceクラスのモックを作成します。
         mock = createMock(IService.class);
         // MockSampleのコンストラクタの引数としてmockオブジェクトを渡します。
         sample = new MockSample(mock);
     }
     @Test public void callEcho() {
         // モックオブジェクトに対して想定される呼び出しを記述します。
         mock.echo();

         // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
         replay(mock);
         sample.execute();
         verify(mock);
     }
     @Test public void callGet() {
         // モックオブジェクトに対して想定される呼び出しと戻り値を記述します。
         expect(mock.get()).andReturn("hoge");

         // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
         // また、戻り値が想定された値かをチェックしています。
         replay(mock);
         String str = sample.getName();
         assertEquals(str, "hoge");
         verify(mock);
     }
 }

このように、モックオブジェクトは、それ自身が正しく呼ばれているか検証することができます。
ただ、内部的にはjava.lang.reflectパッケージのProxyクラスを用いていることから、インターフェースの存在が必須になっているようです。
この点がEasyMockの弱点かなと思います。
また、テスト対象のクラスにモックオブジェクトを登録する必要があります。
うまく登録できる入り口があればよいのですが、この点がテストクラスの作成を困難にすることが多々あります。
この点はモックに限らないことですが。

次はjMockを見ていく予定です。

Comment