1
2

More than 3 years have passed since last update.

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

Posted at

概要

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

前提

今回は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を使いましょう。

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

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

org.mockito.Matchersクラスが非推奨に

このクラスはMockito2系から非推奨になりました。理由はHamcrestにも同名のMatchersクラスがあり、こちらと名前の競合が起こるためです。
Mockito2系からはArgumentMatchersクラスが新設されたのでこちらに移行します。
Mockito3系からMatchersクラスは完全に削除されるので今のうちに移行しましょう。

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

// 移行前
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()を使いましょう。

// テスト対象クラス
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でも型推論により十分のためこちらを利用しましょう。

// テスト対象クラス
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を推論してくれるため、型を明示的に指定せずに呼ぶことができます。

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

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

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

(おまけ)Powermock変更点

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

依存ライブラリバージョンアップと一部変更

修正点はこれだけでした(JUnit4前提)。ただ、依存ライブラリが一部変わるので注意です。

  • powermock-module-junit4のバージョンアップ
  • powermock-api-mockitopowermock-api-mockito2にライブラリを変更しつつバージョンアップ
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>

終わり

1
2
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
1
2