LoginSignup
23
27

More than 5 years have passed since last update.

mockito + PowerMockでfinalメソッドをモック化(再)

Last updated at Posted at 2014-05-03

※一度書いた内容が不完全だったので書き直してます

以下のようなコードで、SocketChannelをモック化して、catch IOExceptionを確認したいと思います。
モックライブラリは「mockito」を使います。

App.java
public class App {
    public void method(SocketChannel channel) throws IOException {
        if (channel.isOpen()) {
            try {
                channel.close();
            } catch (IOException e) {
                // do anything..
            }
        }
    }
}

SocketChannelをモックオブジェクトとして、isOpenとcloseの振る舞いを変えたいところですが、finalメソッドなのでモック化できません。
そこでPowerMockを組み合わせます。

Mavenを使っている場合は、pom.xmlのdependenciesに以下を追加します。

pom.xml
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-all</artifactId>
      <version>1.9.5</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>1.5.4</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>1.5.4</version>
      <scope>test</scope>
    </dependency>

PowerMockの使い方は、https://code.google.com/p/powermock/wiki/MockitoUsage13 を参照してください。

テストコードは以下のようになります。
モックインスタンスの生成にPowerMockito#mock(Class)を使う以外は、普通にmockitoで行うモックの設定方法と同じです(*1)。

AppTest.java
@RunWith(PowerMockRunner.class)
@PrepareForTest({App.class, SocketChannel.class})
public class AppTest {

    @Test
    public void testMethod() throws Exception {
        App target = new App();

        SocketChannel channel =  PowerMockito.mock(SocketChannel.class);
        Mockito.doThrow(new IOException()).when(channel).close();
        Mockito.when(channel.isOpen()).thenReturn(true);

        try {
            target.method(channel);
            fail();
        } catch (Exception e) {
            verify(channel, times(1)).close();
        }
    }

@PrepareForTestにはテスト対象のクラスも指定します(このサンプルの場合はApp.class)。含めないとモックオブジェクトを渡してもモックの動きをしてくれません。
最初、モック対象のクラスだけを含めればよいのかと思っていたら違いました。

*1 「PowerMockito.mock(SocketChannel.class)」を「Mockito.mock(SocketChannel.class)」に変えると以下のように実行時にエラーとなります。
モック化されていないメソッドが呼び出されています。

java.lang.NullPointerException
at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:111)
at sample.AppTest.testA(AppTest.java:38)

23
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
27