Spring Boot 1.5→2系バージョンアップ時のMockito1系→2系の修正内容を取り上げてみた

# 概要
Spring Boot 1.5→2系にバージョンアップした際にMockito周りでコンパイルエラーやDeprecatedのwarningがたくさん出たので調べてみるとSpring Bootが依存するMockitoのバージョンが1系から2系に上がっていることが分かりました。ここではMockito1系から2系にする上で実際に修正した内容をまとめました。（おまけでPowermockのバージョンアップもちょろっと記載しています。）
バージョンアップによって変更された点は他にもありますが、ここでは自分が修正した主要なものを書いています。
その他変更点については以下を参照してください。

[What's new in Mockito 2 · mockito/mockito Wiki](https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2)

# 前提
今回はSpring Bootを以下のようにバージョンアップした前提で書きます。
依存するMockito、Powermockバージョンも以下に記載します。

|dependency|バージョンアップ前|バージョンアップ後|
|:-:|:-:|:-:|
|Spring Boot|1.5.14|2.1.6|
|Mockito|1.10.19|2.23.4|
|Powermock|1.7.1|2.0.2|

# Mockito変更点
## org.mockito.runners.MockitoJUnitRunnerクラスが非推奨に
このクラスはMockito2系から非推奨になりました。Mockito3系で完全に削除されます。
クラス名は同じですが、Mockito2系からは`org.mockito.junit.MockitoJUnitRunner`を使いましょう。

```java
// 移行前
import org.mockito.runners.MockitoJUnitRunner

// 移行後
import org.mockito.junit.MockitoJUnitRunner;
```

## org.mockito.Matchersクラスが非推奨に
このクラスはMockito2系から非推奨になりました。理由はHamcrestにも同名の[Matchers](http://hamcrest.org/JavaHamcrest/javadoc/1.3/)クラスがあり、こちらと名前の競合が起こるためです。
Mockito2系からは[ArgumentMatchers](https://javadoc.io/doc/org.mockito/mockito-core/2.23.4/org/mockito/ArgumentMatchers.html)クラスが新設されたのでこちらに移行します。
Mockito3系からMatchersクラスは完全に削除されるので今のうちに移行しましょう。

ただMockitoにはMockitoクラスというものがあり、Mockito1系ではMatchersクラスを継承していましたが、Mockito2系からArugmentMatchersクラスを継承するようになりました。そのため、最初からMockitoクラスを利用していれば修正の必要はありません。
Matchersクラスから移行する必要がある場合、これを機にMockitoクラスに移行するのもいいかもしれません。

```java
// 移行前
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;

// 移行後
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
// もしくは
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
```

## any(SomeType.class)やanyXX()がnon-nullに
Mockito1系ではnullを許可していましたが、Mockito2系からはnullを許可しなくなりました。そのため、テストで`any(SomeType.class)`や`anyXX()`を指定したところにnullが入る場合はうまくモック化できなかったりアサーションを適切に通せなくなったりします。
良いことではありますが、日々non-nullに対する規制が強くなっていくのを感じます。
もしMockito2系になってテストが落ちたら`anyXX()`で指定していた引数がnon-nullなのかnullableなのか見直しましょう。もしnullableであるならば`any()`や`isNull()`を使いましょう。

```java
// テスト対象クラス
public class TargetClass {
    private final DependencyClass dependency;

    public TargetClass(DependencyClass dependency) {
        this.dependency = dependency;
    }

    public void exec() {
        dependency.call(null);
    }
}

// 依存クラス
public class DependencyClass {
    public void call(String word) {
        if (word != null) {
            System.out.println(word);
        }
    }
}

// テストクラス
@RunWith(MockitoJUnitRunner.class)
public class TargetClassTest {

    @InjectMocks
    private TargetClass target;

    @Mock
    private DependencyClass dependency;

    @Test
    public void testExec() {
        target.exec();

        // Mockito1系ではこのアサーションは通るがMockito2系では通らない
        verify(dependency).call(anyString());
        // Mockito2系ではany()にするかisNull()にする必要がある
        verify(dependency).call(any());
    }
}
```

## anyObject(), anyVararg()が非推奨に
`anyObject()`, `anyVararg()`が非推奨になりました。これらは`any()`で代替できるので、`any()`に置き換えましょう。
Mockito3系からは完全に削除されます。

## anyXXOf(SomeType.class)が非推奨に
`anyListOf(SomeType.class)`や`anyMapOf(SomeType.class)`などのCollection系のMatcherで、かつジェネリクスのクラスを指定するMatcherが非推奨になりました。こちらもMockito3系からは完全に削除されます。
そもそもこれらはListやMapの型を指定するためにキャストしてしまうのを防ぐ目的で用意されたものでしたが、Java8から型推論が改善された(Generalized Target-Type Inference)ため、クラスの指定は不要となり非推奨になりました。
Java8以上を使う場合は`anyList()`や`anyMap()`など、クラス指定のないMatcherでも型推論により十分のためこちらを利用しましょう。

```java
// テスト対象クラス
public class TargetClass {
    private final DependencyClass dependency;

    public TargetClass(DependencyClass dependency) {
        this.dependency = dependency;
    }

    public void exec() {
        dependency.call(Arrays.asList("hello", "world"));
    }
}

// 依存クラス
public class DependencyClass {
    public void call(List<String> words) {
    	words.forEach(System.out::println);
    }
}

// テストクラス
@RunWith(MockitoJUnitRunner.class)
public class TargetClassTest {

    @InjectMocks
    private TargetClass target;

    @Mock
    private DependencyClass dependency;

    @Test
    public void testExec() {
        target.exec();
        // Mockito2系ではこの書き方は非推奨
        verify(dependency).call(anyListOf(String.class));
        // Java8以降であれば型を指定せずとも推論してくれるのでMockito2系からはanyXX()を使用する
        verify(dependency).call(anyList());
    }
}
```

**※Generalized Target-Type Inferenceとは**
調べていく中でGeneralized Target-Type Inferenceについてよく知らなかったので書いておきます。
Java8から型推論のスコープが拡張されより多くの状況で型推論をしてくれるようになりました。Java8から追加された型推論のことをGeneralized Target-Type Inferenceと呼びます。
例えばジェネリクスを使用したメソッドを呼ぶ場合に、コンパイラが型変数Tを推論してくれるため、型を明示的に指定せずに呼ぶことができます。

```java
List<String> stringList = new ArrayList<>();
stringList.add("A");
//上記によりstringListがString型のリストだと推論されるため、特に型を指定せずともOK
stringList.addAll(Arrays.asList());
```

Java7では上記のような型推論ができないためこのように書く必要がありました。

```java
List<String> stringList = new ArrayList<>();
stringList.add("A");
//型を指定する必要がある
stringList.addAll(Arrays.<String>asList());
```

- [JEP 101: Generalized Target-Type Inference](http://openjdk.java.net/jeps/101)
- [Javaプログラミング言語の拡張機能](https://docs.oracle.com/javase/jp/8/docs/technotes/guides/language/enhancements.html)
- [Generalized Target-Type Inference in Java | Baeldung](https://www.baeldung.com/java-generalized-target-type-inference)
- [Why does the Java 8 generic type inference pick this overload? - Stack Overflow](https://stackoverflow.com/questions/30521974/why-does-the-java-8-generic-type-inference-pick-this-overload/30526908#30526908)

# （おまけ）Powermock変更点
こちらはSpring Bootのバージョンアップに依存しているというより、Mockitoのバージョンアップに依存していますが、Mockito2系に上げたことによりPowermockのバージョンも上げる必要があったので書いておきます。

## 依存ライブラリバージョンアップと一部変更
修正点はこれだけでした(JUnit4前提)。ただ、依存ライブラリが一部変わるので注意です。

- `powermock-module-junit4`のバージョンアップ
- `powermock-api-mockito`→`powermock-api-mockito2`にライブラリを変更しつつバージョンアップ

```xml:pom.xml
<!-- バージョンアップ前 -->
<properties>
    <powermock.version>1.7.1</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>

<!-- バージョンアップ後 -->
<properties>
    <powermock.version>2.0.2</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito2</artifactId> <!-- このライブラリが変わる -->
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>
```

- [Mockito · powermock/powermock Wiki](https://github.com/powermock/powermock/wiki/Mockito)

終わり