本ページはプロモーションが含まれています

Java入門

Mockitoの使い方|JUnit5でのモック・spy・verify

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

「whenとverifyはなんとなく書けるけど、spyやArgumentCaptorは結局いつ使うのか分からない」。Mockitoを触り始めると、そんな壁にぶつかりやすいです。

Mockitoとは、Javaのユニットテストで依存オブジェクトを偽物(モック)に置き換えるためのライブラリです。使い方の基本は、@ExtendWith(MockitoExtension.class)を付けたテストクラスで、(1)@Mockでモックを作り、(2)@InjectMocksでテスト対象に注入し、(3)when()で戻り値を決めてverify()で呼び出しを確かめる、の3ステップです。

この記事では、Mockito 5.24.0とJUnitで実際にコンパイル・実行を確認したコードを使い、最小構成のテストクラスから、doReturn・spy・ArgumentCaptor・mockStaticの使い分け、Java 21以降で出る警告の対処、Spring Bootの@MockitoBeanまで順に整理します。

この記事でわかること

  • Mockitoの基本(モックとスタブの違い、導入方法)
  • JUnit5で動く最小構成のテストクラス(@Mock・@InjectMocks)
  • when・verifyと引数マッチャー(any・eq)の正しい使い方
  • doReturn・spy・ArgumentCaptorの使いどころ
  • 静的メソッドをモック化するmockStaticの注意点とJava 21以降の警告対策
  • Spring Bootの@MockitoBeanへの移行方法

Mockitoとは?モックテストの基本

Mockitoは、Javaで広く使われているモックライブラリです。

クラスがデータベースや外部APIに依存していると、ユニットテストのたびに本物の接続先が必要になり、テストが遅く不安定になります。Mockitoは、その依存先を偽物のオブジェクトに置き換えることで、テスト対象のロジックだけを切り出して検証できるようにします。

モックとスタブの違い

Mockitoの用語には、似ているが役割が異なるものが混在しています。

用語役割
モック(Mock)本物の代わりに使う偽物のオブジェクト。呼び出されたかどうかを検証できる
スタブ(Stub)あらかじめ決めた値を返すよう設定すること。when().thenReturn()で行う
スパイ(Spy)実オブジェクトをベースに、一部のメソッドだけ振る舞いを差し替える

「モックを作って、スタブとして振る舞いを設定する」という関係を押さえておくと、以降のメソッドの意味が理解しやすくなります。

導入方法(Maven/Gradle)

Mockitoの導入は、ビルドツールに依存関係を1つ追加するだけで完了します。

xml
<!-- Mavenの場合(pom.xml) -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>5.24.0</version>
    <scope>test</scope>
</dependency>
groovy
// Gradleの場合(build.gradle)
testImplementation "org.mockito:mockito-junit-jupiter:5.24.0"

mockito-junit-jupiterを入れておけば、本体のmockito-coreと、JUnitと組み合わせて使うための拡張機能(MockitoExtension)が一緒に導入されます。バージョンは2026年9月時点の5.24.0をもとにしています。Mockito 5はJava 11以上で動作し、JUnit 5・JUnit 6のどちらとも組み合わせられます(筆者の手元ではJUnit 6.1.3で実行を確認)。

あわせて読む
Mockitoの基本:when で仕込み、verify で確認
テスト対象
本物のクラス
→
モック
偽の依存オブジェクト
when(…).thenReturn(値) → 返す値を先に決める(スタブ)verify(…) → 呼ばれ方を後から確認する

JUnit5でMockitoを使う最小構成(@Mock・@InjectMocks)

まずは、そのまま動く最小構成のテストクラスを見てください。題材は「リポジトリから価格を取得して税込価格を返すサービス」で、リポジトリ(本番ではDBにアクセスする部分)をモックに置き換えます。

java
// テスト対象が依存するリポジトリ(本番ではDBにアクセスする想定)
public interface PriceRepository {
    int findPrice(int itemId);
}

// テスト対象のサービス
public class PriceService {
    private final PriceRepository priceRepository;

    public PriceService(PriceRepository priceRepository) {
        this.priceRepository = priceRepository;
    }

    // 税込価格(10%)を返す
    public int getPriceWithTax(int itemId) {
        int price = priceRepository.findPrice(itemId);
        return (int) (price * 1.1);
    }
}

このサービスをテストするコードが次のとおりです。

java
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class) // @Mock・@InjectMocksを有効にする
class PriceServiceTest {

    @Mock
    private PriceRepository priceRepository; // 偽物の依存先

    @InjectMocks
    private PriceService priceService; // テスト対象(モックが自動で注入される)

    @Test
    void 税込価格を計算できること() {
        // 準備:ID=1のときは1000円を返す
        when(priceRepository.findPrice(1)).thenReturn(1000);

        // 実行
        int price = priceService.getPriceWithTax(1);

        // 検証:戻り値と、依存先の呼ばれ方
        assertEquals(1100, price);
        verify(priceRepository).findPrice(1);
    }
}

ポイントは3つのアノテーションです。

アノテーション役割
@ExtendWith(MockitoExtension.class)JUnitにMockitoの拡張を組み込み、@Mock・@InjectMocksを有効にする
@Mockフィールドの型のモックを自動で生成する
@InjectMocksテスト対象を生成し、@Mockのモックをコンストラクタ等に注入する

MockitoExtensionはデフォルトで「使われなかったスタブ」を検出してテストを失敗させます(UnnecessaryStubbingException)。テストに不要なwhenを書いたまま放置しないための仕組みなので、このエラーが出たら該当のwhenを削除すればよいです。

このサンプルは、筆者の手元(JDK 23、Mockito 5.24.0、JUnit 6.1.3)でコンパイル・実行してテストが通ることを確認しています。JUnit自体の書き方は、JUnit5入門の記事で解説しています。

when・verifyの基本と引数マッチャー

Mockitoの基本操作は、振る舞いを設定するwhenと、呼び出しを検証するverifyの2つに集約されます。

when().thenReturn()でスタブを定義する

whenは「このメソッドがこの引数で呼ばれたら、この値を返す」という約束を先に決めておく仕組みです。

java
// 商品ID=1のときは1000円を返すよう設定
when(priceRepository.findPrice(1)).thenReturn(1000);

int price = priceService.getPriceWithTax(1);

assertEquals(1100, price);

データベースに1件も登録せずに、「価格が1000円のときにサービス側がどう動くか」だけを検証できます。

あわせて読む

verify()で呼び出しを検証する

verifyは「そのメソッドが期待通りに呼ばれたか」を後から確認する仕組みです。

java
// findPriceが1回だけ呼ばれたことを検証
verify(priceRepository, times(1)).findPrice(1);

// 一度も呼ばれていないことを検証
verify(priceRepository, never()).findPrice(2);

戻り値を検証するだけでは気づけない不具合(例えば、同じメソッドを意図せず2回呼んでいる等)を検出できるのがverifyの強みです。

引数マッチャー(any・eq)の使い方

「どんな引数で呼ばれても同じ値を返したい」ときは、anyInt()・any()などの引数マッチャーを使います。

java
// どの商品IDで呼ばれても500円を返す
when(priceRepository.findPrice(anyInt())).thenReturn(500);

assertEquals(550, priceService.getPriceWithTax(99));

// 引数の値を指定して検証(マッチャーを使うなら全引数をマッチャーにする)
verify(priceRepository).findPrice(eq(99));

引数が複数あるメソッドでマッチャーを1つでも使う場合、残りの引数もすべてマッチャーにする必要があります。save(anyInt(), "A")のように生の値と混ぜるとInvalidUseOfMatchersExceptionになるため、値を固定したい引数はeq("A")で包みます。

doReturn・doThrowを使う場面

doReturn・doThrowは、whenが使えない場面で登場します。

whenより安全なケースとその理由

voidを返すメソッドには、そもそもwhen(mock.method())という書き方ができません。戻り値がないため、whenに渡す式を作れないからです。

java
// voidメソッドが例外を投げるよう設定する
doThrow(new IllegalStateException("在庫切れ"))
    .when(stockService).reserve(anyInt());

なお、モックのvoidメソッドは何も設定しなくても「何もしない」ため、doNothing()を明示的に書く場面は多くありません。doNothing()が必要になるのは、主にspyのvoidメソッドで本物の処理を止めたいときや、連続呼び出しで「1回目は何もせず、2回目は例外」のように振る舞いを切り替えたいときです。

もう1つ重要な使い分けがあります。spyに対してwhenを使うと、実オブジェクトの本物のメソッドが一度実際に呼ばれてしまう場合があります。副作用のあるメソッド(DB書き込み等)をスパイする際は、doReturnを使って本物の呼び出し自体を避けます。

  • 通常のモック:when().thenReturn()で基本問題ない
  • voidメソッド:doThrow().when()のようにdo系を使う
  • spyの一部メソッド:本物を呼ばせたくないならdoReturn().when()を使う

spyで実オブジェクトの一部だけモック化する

spyは、実オブジェクトをベースに一部のメソッドだけ振る舞いを差し替えたいときに使います。

mock()とspy()の違い

mockで作ったオブジェクトは、設定していないメソッドを呼ぶと基本的に何もしないか、型のデフォルト値(参照型はnull、数値は0、booleanはfalse。コレクションは空、OptionalはOptional.empty())を返します。

一方、spyで作ったオブジェクトは実物そのものなので、設定していないメソッドを呼ぶと本物のロジックがそのまま動きます。

mock()spy()
ベース完全な偽物実オブジェクト
未設定メソッドの動作デフォルト値を返す本物のロジックが実行される
向いている場面依存先を丸ごと切り離したいとき一部のメソッドだけ差し替えたいとき

実務でクラスを丸ごとリファクタリングした際、一部のメソッドだけspyで差し替えていたテストが、本物のロジック変更につられて意図せず壊れたことがあります。spyは便利ですが、本物のコードと結びついている分だけテストが脆くなりやすいです。基本はmockを使い、既存クラスの一部だけ差し替えたい理由が明確なときに限ってspyを選ぶのが安全です。

mockとspyの違い
mock()
完全な偽物を作る
指定しないメソッドは何もせず
null や 0 を返す
spy()
本物のオブジェクトを包む
指定したメソッドだけ差し替え
ほかは本物の処理が動く

ArgumentCaptorで渡された引数を検証する

ArgumentCaptorは、モックのメソッドに実際に渡された引数の中身を後から取り出して検証するための仕組みです。

「呼ばれたかどうか」はverifyで分かりますが、「どんな値で呼ばれたか」まで細かく検証したいときに使います。

java
ArgumentCaptor<Order> captor = ArgumentCaptor.forClass(Order.class);

verify(orderRepository).save(captor.capture());

Order savedOrder = captor.getValue();
assertEquals("pending", savedOrder.getStatus());

保存メソッドに渡されたOrderオブジェクトの中身まで踏み込んで検証できるため、「保存はされたが、ステータスの設定を忘れていた」といったバグを見逃しにくくなります。

mockStaticで静的メソッドをモック化する

Mockitoは長らく静的メソッドのモック化に対応していませんでしたが、3.4.0でmockStaticが追加されました。現行のMockito 5なら、依存関係を追加しなくてもそのまま使えます。

Mockito 5ではmockito-inlineの追加は不要

Mockito 4までの標準の仕組みは、対象クラスのサブクラスを動的に生成してモックを作るものでした。静的メソッドやfinalクラスはこの方法では差し替えられないため、以前はバイトコードを直接書き換えるmockito-inlineを別途追加する必要がありました。

Mockito 5.0.0からは、この「インライン方式」がデフォルトになりました。そのため現在はmockito-core(mockito-junit-jupiter経由で入るもの)だけでmockStaticもfinalクラスのモックも使えます。mockito-inlineの配布は5.2.0(2023年3月)で止まっており、古い記事にあるmockito-inlineの依存関係はMockito 5では追加しなくてよいです。

テストで「今日の日付」を固定したいときの例が次のコードです。

java
LocalDate fixed = LocalDate.of(2026, 1, 1); // モック化する前に作っておく

try (MockedStatic<LocalDate> mocked = mockStatic(LocalDate.class)) {
    mocked.when(LocalDate::now).thenReturn(fixed);

    assertEquals(fixed, LocalDate.now());
}
// tryを抜けると本物のLocalDate.now()に戻る

mockStaticはtry-with-resourcesのスコープを抜けると自動的に元の状態へ戻ります。スコープの外で使うと他のテストに影響が漏れるため、必ずtryブロック内に収めてください。静的メソッドの多用は設計上の問題のサインでもあるため、乱用は避けたいところです。

もう1つの落とし穴は、thenReturn(LocalDate.of(2026, 1, 1))のように、モック化したクラス自身の静的メソッドをスタブ定義の中で呼ぶことです。LocalDate.ofもモック化されているため、実行するとUnfinishedStubbingExceptionで失敗します。上のコードのように、返したい値はtryの前に作っておきます。

Java 21以降で出る警告とエージェント設定

Java 21以降でMockito 5のテストを実行すると、テストは成功しても次のような警告が出ます。筆者の手元(JDK 23)で上のサンプルを実行したときの出力です。

text
Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: ...
WARNING: A Java agent has been loaded dynamically (.../byte-buddy-agent-1.17.7.jar)
WARNING: Dynamic loading of agents will be disallowed by default in a future release

インライン方式のモックは、実行中のJVMにJavaエージェントを動的に読み込ませて実現しています。JDK 21(JEP 451)から、この動的読み込みに警告が出るようになり、将来のJDKではデフォルトで禁止される予定です。Mockito公式ドキュメントは、テスト実行時にMockitoをエージェントとして明示的に指定する方法を案内しています。

xml
<!-- pom.xml:${org.mockito:mockito-core:jar} を使えるようにする -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <goals>
                <goal>properties</goal>
            </goals>
        </execution>
    </executions>
</plugin>

<!-- テスト実行時にMockitoをエージェントとして読み込む -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>@{argLine} -javaagent:${org.mockito:mockito-core:jar}</argLine>
    </configuration>
</plugin>

@{argLine}は、JaCoCoなど他のプラグインが設定したargLineを引き継ぐための記述です。argLineをどこでも定義していないプロジェクトでは文字列がそのままJVMに渡ってエラーになるため、その場合は@{argLine} を外して-javaagent:...だけを書きます。Gradle(Kotlin DSL)なら次のように設定します。

kotlin
// build.gradle.kts
val mockitoAgent = configurations.create("mockitoAgent")

dependencies {
    testImplementation("org.mockito:mockito-junit-jupiter:5.24.0")
    mockitoAgent("org.mockito:mockito-core:5.24.0") { isTransitive = false }
}

tasks.test {
    jvmArgs("-javaagent:${mockitoAgent.asPath}")
}

筆者の手元でも、-javaagentでMockitoを指定して実行すると、同じテストが警告なしで通ることを確認しました。-XX:+EnableDynamicAgentLoadingを付けて警告を消す方法もあります。将来のJDKで動的読み込みがデフォルト禁止になっても、このオプションを付ければ許可し続けられますが、動的読み込みを許可したままにする設定なので、エージェント指定の方をおすすめします。

Spring Bootの@MockitoBeanへの移行

Spring Bootでテストを書いてきた人には馴染み深い@MockBeanは、Spring Boot 3.4で非推奨になり、Spring Boot 4.0で削除されました。

@MockBeanは3.4で非推奨、4.0で削除

@MockBean・@SpyBeanはSpring Boot 3.4で非推奨となり、Spring Framework自体が提供する@MockitoBean・@MockitoSpyBeanへの置き換えが推奨されました。予告どおりSpring Boot 4.0で削除されたため、4.0以降に上げたプロジェクトでは@MockBeanのままだとコンパイルが通りません。import先もorg.springframework.test.context.bean.override.mockitoパッケージに変わります。

旧アノテーション新アノテーション提供元
@MockBean@MockitoBeanSpring Framework
@SpyBean@MockitoSpyBeanSpring Framework
java
@WebMvcTest(UserController.class)
class UserControllerTest {

    @MockitoBean
    private UserService userService;

    @Autowired
    private MockMvc mockMvc;
}

書き方自体は@MockBeanとほぼ同じですが、1点だけ注意が必要です。@MockitoBeanは@Configurationクラスには使えない仕様になっています。@Configurationクラスでモックを差し込んでいた既存コードは、テストクラスのフィールドへ書き直す移行作業が必要になります。

よくある質問

Q1. Mockitoとは何ですか?

A1. Javaのユニットテストで、依存オブジェクトを偽物(モック)に置き換えるためのライブラリです。データベースや外部APIに接続せずに、テスト対象のロジックだけを検証できるようにします。

Q2. mockとspyの使い分けは?

A2. 依存先を丸ごと切り離したいならmock、実オブジェクトの一部のメソッドだけ差し替えたいならspyを使います。spyは本物のロジックと結びつく分、テストが壊れやすくなる点に注意してください。

Q3. whenとdoReturnはどう違いますか?

A3. 基本はwhen().thenReturn()を使います。voidメソッドの設定や、spyで本物のメソッドを呼ばせたくない場合はdoReturn().when()のようなdo系の構文を使います。

Q4. 静的メソッドをモック化するにはどうすればいいですか?

A4. Mockito 5なら追加の依存関係なしでmockStatic()を使えます(Mockito 4以前はmockito-inlineの追加が必要でした)。try-with-resourcesのスコープ内で使い、スコープを抜けたら自動的に元の状態に戻ります。

Q5. @MockBeanは使ってはいけませんか?

A5. Spring Boot 3.4〜3.5では非推奨ながら動作しますが、Spring Boot 4.0で削除されたため4.0以降では使えません。新規に書くテストは@MockitoBeanを使い、3.x系の既存コードも4.0へ上げる前に置き換えておきましょう。

Q6. ArgumentCaptorはverifyと何が違いますか?

A6. verifyはメソッドが呼ばれたかどうかを検証します。ArgumentCaptorは、そのメソッドに実際に渡された引数の中身まで取り出して検証したいときに使います。

まとめ:まずはwhen・verifyから覚える

Mockitoは、依存オブジェクトを偽物に置き換えてロジックだけを検証するためのライブラリです。この記事で扱った機能の使い分けを図にすると、次のようになります。

flowchart TD A["依存先をテストで差し替えたい"] --> B{"依存先を丸ごと<br>偽物にしてよい?"} B -->|"はい(基本)"| C["mock()/@Mock"] B -->|"いいえ、一部だけ差し替えたい"| D["spy()"] C --> E{"差し替えるメソッドは?"} D --> F["doReturn().when()<br>(本物を呼ばせない)"] E -->|"戻り値あり"| G["when().thenReturn()"] E -->|"voidメソッド"| H["doThrow()/doNothing()"] G --> I{"実行後に何を確かめる?"} F --> I H --> I I -->|"呼ばれたか・回数"| J["verify()"] I -->|"渡された引数の中身"| K["ArgumentCaptor"]

ポイント

  • 基本はwhen・verifyで十分カバーできる
  • voidメソッドやspyにはdoReturn/doThrowを使う
  • spyは便利だが本物のコードと結びつく分、壊れやすい
  • 引数の中身まで検証したいときはArgumentCaptor
  • Spring Boot 4.0以降は@MockBeanが削除済み。@MockitoBeanを使う

いきなり全部を使いこなそうとせず、when・verifyで基本を固めてから、必要になったタイミングでspyやArgumentCaptorを足していくのが、テストを壊さずに済む一番の近道です。

なお、「どこまでモックに置き換えるべきか」は、単体テストの流派によって考え方が分かれます。依存先を積極的にモックにするロンドン学派と、なるべく本物を使う古典学派の違いは、次の記事で整理しています。

あわせて読む
REVISIONS / 改訂履歴
REV.DATE内容
22026-10-04記載内容の事実確認と修正
12026-09-14初版
  • この記事を書いた人
  • 最新記事

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

-Java入門