MockitoとPowerMockの使い分け

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

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

```groovy: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を使用する設定を加えておきます。

----
# 説明に使うクラス
## テストの対象となるクラス
```java: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されるサブクラス)

```java: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が使えます。

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

  @Test
  public void テストメソッド() {
    .....
  }
}
```
----
### publicメソッドの実際の挙動のテスト
書くまでもないですが、テストコードはこちら

```java
  @Test
  public void publicメソッドを呼び出してテストできる() {
    assertThat(public_method(), is("public_method called public_sub"));
  }
```
----
### privateメソッドの実際の挙動のテスト
リフレクションを使ってsetAccessibleを使えばOK

```java
  @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メソッドの実際の挙動のテスト
書くまでもないですが、テストコードはこちら

```java
  @Test
  public void staticメソッドを呼び出してテストできる() {
    assertThat(TestTargetClass.static_method(), is("static_method"));
  }
```
----
ここからが本題のモックを使ったテストです。

----
## Mockitoだけでできるテスト
### publicメソッドをモックする場合
このようなときには`Mockito.mock()`を使用します。

```java
  @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()`を使用します。

```java
  @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を使うときのテストクラスはこんな感じ

```java
@RunWith(PowerMockRunner.class)
@PrepareForTest({TestTargetClass.class})
public class PowerMockTest {
  @Test
  public void テストメソッド() {
    .....
  }
}
```
----
### privateメソッドのモック
`PowerMockito.mock()`と`PowerMockito.when()`を使います。またprivateメソッドを指定するため`MemberMatcher.method()`を使ってレフレクションします。

```java
  @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()`を使います。

```java

  @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()`を使ってレフレクションします。

```java
  @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()`します。

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

```java
@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メソッドなのでテストクラスでサブクラスを宣言している箇所は

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

```java
  @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()`を使ってレフレクションします。

```java
  @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()`を使います。

```java 
  @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フィールドにセットする場合と同じです。

```java 
  @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"));
  }
```

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