LoginSignup
91

More than 5 years have passed since last update.

MockitoとPowerMockの使い分け

Posted at
1 / 23

SpringBootでJUnitでテストをするときの様々なメソッドをモックするときにMockitoとPowerMockの使い分けを整理してみました。

テストをする際に必要な設定

build.gradle
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile('org.springframework.boot:spring-boot-starter-test')
    testCompile group: 'org.powermock', name: 'powermock-api-mockito', version: '1.6.6'
    testCompile group: 'org.powermock', name: 'powermock-module-junit4', version: '1.6.6'
}

spring-boot-starter-testを入れるだけで、以下の二つのライブラリが使用可能になります。
バージョンはspring-boot-gradle-plugin:1.5.2.RELEASEのものです。

  • junit-4.12.jar
  • mockito-core-1.10.19.jar

これに、後ほど使用するpowermockを使用する設定を加えておきます。


説明に使うクラス

テストの対象となるクラス

TestTargetClass.java
@Component
public class TestTargetClass {
  @Autowired
  private TestSubClass testSubClass;

  private String private_method() {
    return "private_method called " + private_sub();
  }

  private String private_sub() {
    return "private_sub";
  }

  public String public_method() {
    return "public_method called " + public_sub();
  }

  public String public_sub() {
    return "public_sub";
  }

  public String public_method_call_private_method() {
    return "public_method called " + private_sub();
  }

  public static String static_method() {
    return "static_method";
  }

  public String public_subclass_public_method() {
    return "public_subclass_method called " + testSubClass.sub_public_method();
  }

  public String public_subclass_private_method() {
    return "public_subclass_method called " + testSubClass.sub_public_method_call_private_method();
  }
}

テストの対象となるサブクラス(親クラスからDIされるサブクラス)

TestSubClass.java
@Component
public class TestSubClass {

  public String public_field = "public_field";
  private String private_field = "private_field";

  private String sub_private_method() {
    return "subclass_private_method has " + private_field;
  }

  public String sub_public_method() {
    return "subclass_public_method has " + public_field;
  }

  public String sub_public_method_call_private_method() {
    return "subclass_public_method called " + sub_private_method();
  }
}

まずは基本のテストから

テストクラスはこんな感じでMockitoが使えます。

RealTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class RealTest {
  @Autowired
  private TestTargetClass instance;

  @Test
  public void テストメソッド() {
    .....
  }
}

publicメソッドの実際の挙動のテスト

書くまでもないですが、テストコードはこちら

  @Test
  public void publicメソッドを呼び出してテストできる() {
    assertThat(public_method(), is("public_method called public_sub"));
  }

privateメソッドの実際の挙動のテスト

リフレクションを使ってsetAccessibleを使えばOK

  @Test
  public void privateメソッドを呼び出してテストできる() throws Exception {
    Method method = TestTargetClass.class.getDeclaredMethod("private_method");
    method.setAccessible(true);
    assertThat((String) method.invoke(instance), is("private_method called private_sub"));
  }

staticメソッドの実際の挙動のテスト

書くまでもないですが、テストコードはこちら

  @Test
  public void staticメソッドを呼び出してテストできる() {
    assertThat(TestTargetClass.static_method(), is("static_method"));
  }

ここからが本題のモックを使ったテストです。


Mockitoだけでできるテスト

publicメソッドをモックする場合

このようなときにはMockito.mock()を使用します。

  @Test
  public void publicメソッドをモックできる() {
    TestTargetClass mockInstance = mock(TestTargetClass.class);
    when(mockInstance.public_method()).thenReturn("mocked_public_method");
    assertThat(mockInstance.public_method(), is("mocked_public_method"));
  }

publicメソッドから呼ばれるサブのpublicメソッドをモックする場合

モックを行うメソッドpublic_sub()以外のメソッドは実装通りに動いてほしいので、このようなときにはMockito.spy()を使用します。

  @Test
  public void publicメソッドが呼ぶSubメソッドはSpyでモックできる() {
    TestTargetClass mockInstance = spy(new TestTargetClass());
    when(mockInstance.public_sub()).thenReturn("mocked_public_sub");
    assertThat(mockInstance.public_method(), is("public_method called mocked_public_sub"));
  }

PowerMockでできるテスト

PowerMockを使うときのテストクラスはこんな感じ

@RunWith(PowerMockRunner.class)
@PrepareForTest({TestTargetClass.class})
public class PowerMockTest {
  @Test
  public void テストメソッド() {
    .....
  }
}

privateメソッドのモック

PowerMockito.mock()PowerMockito.when()を使います。またprivateメソッドを指定するためMemberMatcher.method()を使ってレフレクションします。

  @Test
  public void privateメソッドはPowerMockitoでモックできる() throws Exception {
    TestTargetClass mockInstance = PowerMockito.mock(TestTargetClass.class);
    PowerMockito.when(mockInstance, MemberMatcher.method(TestTargetClass.class, "private_method"))
                .withNoArguments().thenReturn("mocked_private_method");

    // privateメソッドにアクセスするためにリフレクションを使う
    Method method = TestTargetClass.class.getDeclaredMethod("private_method");
    method.setAccessible(true);

    assertThat((String) method.invoke(mockInstance), is("mocked_private_method"));
  }

staticメソッドのモック

PowerMockito.mockStatic()PowerMockito.when()を使います。


  @Test
  public void staticメソッドはPowerMockitoでモックできる()() {
    PowerMockito.mockStatic(TestTargetClass.class);
    PowerMockito.when(TestTargetClass.static_method()).thenReturn("mocked_static_method");
    assertThat(TestTargetClass.static_method(), is("mocked_static_method"));
  }

publicメソッドから呼ばれるサブのprivateメソッドをモックする場合

モックを行うメソッドprivate_sub()以外のメソッドは実装通りに動いてほしいので、このようなときにはPowerMockito.spy()PowerMockito.when()を使用します。またprivateメソッドを指定するためMemberMatcher.method()を使ってレフレクションします。

  @Test
  public void publicメソッドから呼ばれるサブのprivateメソッドはspyでモックできる() throws Exception {
    TestTargetClass mockInstance = PowerMockito.spy(new TestTargetClass());
    PowerMockito.when(mockInstance, MemberMatcher.method(TestTargetClass.class, "private_sub"))
                .withNoArguments().thenReturn("mocked_private_sub");

    assertThat(mockInstance.public_method_call_private_method(),
        is("public_method called mocked_private_sub"));
  }

クラスでDIされているクラスをモックする場合

コントローラークラスでDIしているサービスクラスに対してモックを行う様なケースを想定しています
この時もPowerMockを使用します。
@SpringBootTestアノテーションで@InjectMocksで指定したクラスに対して中で指定している@Autowiredの対象クラスをDIします。
@Autowiredの対象クラスはクラス変数としてPowerMockito.spy()します。


テストクラスはこんな感じ

@RunWith(PowerMockRunner.class)
// PowerMock対象のクラス
@PrepareForTest({TestSubClass.class})
@SpringBootTest
public class PowerMockSubclassTest {
  @InjectMocks
  private TestTargetClass instance;
  private TestSubClass testSubClass = PowerMockito.spy(new TestSubClass());

  @Test
  public void テストメソッド() {
    .....
  }
}

クラス変数ではなく下のようにしてもモックはできないようです。 ... 多分
何か良い方法があったら是非コメントください。

  @Test
  public void テストメソッド() {
     TestSubClass testSubClass = PowerMockito.spy(new TestSubClass());
  }

サブクラスのpublicメソッドをモック

本当はpublicメソッドなのでテストクラスでサブクラスを宣言している箇所は

private TestSubClass testSubClass = PowerMockito.mock(TestSubClass.class);

でもよいのですが、同じソースファイル内でspy()を使用したものといっしょに使えないのでspy()の方で統一しておきます。
PowerMockito.when()を使います。

  @Test
  public void サブクラスのpublicメソッドをモックできる() {
    PowerMockito.when(testSubClass.sub_public_method()).thenReturn("mocked_subclass_public_method");
    assertThat(instance.public_subclass_public_method(),
        is("public_subclass_method called mocked_subclass_public_method"));
  }

サブクラスのprivateメソッドをモック

PowerMockito.when()を使います。またprivateメソッドを指定するためMemberMatcher.method()を使ってレフレクションします。

  @Test
  public void サブクラスのprivateメソッドをモックできる() throws Exception {
    PowerMockito.when(testSubClass, MemberMatcher.method(TestSubClass.class, "sub_private_method"))
        .withNoArguments().thenReturn("mocked_sub_private_method");

    assertThat(instance.public_subclass_private_method(), is(
        "public_subclass_method called subclass_public_method called mocked_sub_private_method"));
  }

サブクラスのpublicフィールドをモック

サブクラスのフィールドにモックをセットする場合はWhitebox.setInternalState()を使います。

  @Test
  public void サブクラスのpublicフィールドをモックできる() {
    Whitebox.setInternalState(testSubClass, "public_field", "mocked_public_field");
    assertThat(instance.public_subclass_public_method(),
        is("public_subclass_method called subclass_public_method has mocked_public_field"));
  }

サブクラスのprivateフィールドをモック

サブクラスのフィールドにモックをセットする場合はWhitebox.setInternalState()を使います。
publicフィールドにセットする場合と同じです。

  @Test
  public void サブクラスのprivateフィールドをモックできる() {
    Whitebox.setInternalState(testSubClass, "private_field", "mocked_private_field");
    assertThat(instance.public_subclass_private_method(), is(
        "public_subclass_method called subclass_public_method called subclass_private_method has mocked_private_field"));
  }

長文になりましたが、以上です。

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
91