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


前回はEasyMockについて簡単なサンプルを作って見ました。
今回はjMockで同じような事を試して、EasyMockとどう違うか見ていきます。

まずjMockのサイトからライブラリをダウンロードするなり、Mavenを使っているならpom.xmlを以下のように編集します。
 <dependency>
       <groupId>org.jmock</groupId>
       <artifactId>jmock</artifactId>
       <version>2.5.1</version>
     </dependency>
 </dependency>
テスト対象のクラスは前回と同じです。再掲します。
 package test.easymock;

 public class MockSample {
     private IService service;
     public MockSample(IService service) {
         this.service = service;
     }
     public void execute() {
         service.echo();
     }
     public String getName() {
         return service.get();
     }
     public interface IService {
         public void echo();
         public String get();
     }
 }
モックオブジェクとを作成して、テスト対象のクラスに注入して、適切に呼ばれているかをテストするという流れはEasyMockと同様です。
想定される呼び出しの記述にインスタンス初期化子を使っている所が個人的には見やすく感じました。
戻り値の指定も同様にインスタンス初期化子の中に記述します。
 package test.easymock;

 import org.jmock.Expectations;
 import org.jmock.Mockery;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;

 import test.easymock.MockSample.IService;

 public class TestJMockSample {
     private Mockery context;
     private MockSample sample;
     private IService service;
     @BeforeMethod
     public void setupMock() {
         // IServiceクラスのモックを作成します。
         context = new Mockery();
         service = context.mock(IService.class);
         // MockSampleのコンストラクタの引数としてmockオブジェクトを渡します。
         sample = new MockSample(service);
     }
     @Test public void callEcho() {
         // モックオブジェクトに対して想定される呼び出しを記述します。
         context.checking(new Expectations() {
             {oneOf(service).echo();
             }});
        
         // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
         sample.execute();
         context.assertIsSatisfied();
     }
     @Test public void callGet() {
         // モックオブジェクトに対して想定される呼び出しと戻り値を記述します。
         context.checking(new Expectations() {
             {oneOf(service).get();
             will(returnValue("hoge"));
             }});
        
         // 実際にクラスを実行してモックオブジェクトが想定通り呼び出されるかチェックします。
         // また、戻り値が想定された値かをチェックしています。
         assertEquals(sample.getName(), "hoge");
         context.assertIsSatisfied();
     }
 }
ドキュメントを読む限りEasyMockと機能的にはそれほど大差ないのではないかと思ったのですが、jMockではインターフェースのないクラスもテストすることができます。
jMockでは内部的に使用しているProxyクラスに関して、reflectionを使う場合とcglibを使う場合に切り替ることができます。
cglibを使うことで、インターフェースのないクラスのモックオブジェクトを作成出きるようになっています。

インターフェースのないクラスのモックオブジェクトを作る場合は、pom.xmlに下記を追加します。
 <dependency>
   <groupId>org.jmock</groupId>
   <artifactId>jmock-legacy</artifactId>
   <version>2.5.1</version>
 </dependency>    
インターフェースのないクラスをテストするときは、Mockeryクラスを生成するときに、インスタンス初期化で下記の様に指定します。
 context = new Mockery() {{
    setImposteriser(ClassImposteriser.INSTANCE);
 }};

この様に、jMockはEasyMockと比べるとインスタンス初期化子で記述する点や、インターフェースがなくてもモックオブジェクトを作成できる点で、優れている所も多そうです。

Comment