Java

PowerMockの@PrepareForTest指定ルールとPowerMockRunnerの設定手順

PowerMockの@PrepareForTest指定ルールとPowerMockRunnerの設定手順

PowerMockitoでつまずく箇所は、APIの書き方よりも@PrepareForTestにどのクラスを書くかに集中します。指定を1つ間違えるだけで、モックが素通りして実メソッドが走り、テストは「なぜか本物が呼ばれる」状態で落ちます。ここでは@PrepareForTestの指定ルール、PowerMockRunnerと依存定義、静的メソッドの検証記法を、公式wikiとソースコードの記述で整理します。PowerMockitoそのものの位置づけやMockitoネイティブへ移行すべきかの判断は、PowerMockitoとは?静的・privateメソッドのMock化とMockitoネイティブへの移行判断にまとめてあります。

まとめ

  • @PrepareForTestには原則としてモック対象の静的メソッドを定義しているクラスを書きます。
  • 例外は2つです。java.*などのシステムクラスと、whenNewによるコンストラクタのモックでは呼び出す側(newする側)のクラスを指定します。
  • 依存はPowerMockの2アーティファクトに加えてMockito本体とJUnit 4が必要です。PowerMock 2.0.9はmockito-core 3.3.3でビルドされているため、Mockitoは2.8.9以上3.x系までに収めます。
  • verifyStaticは「verifyStaticでクラスを指定する行」と「検証したい静的メソッドを書く行」の2行で1組です。引数なしのverifyStatic()は2.0で削除されており、2.x系ではコンパイルできません。
  • PowerMockの最終リリースは2.0.9(2020年11月2日)、リリースブランチの最終コミットは2022年2月24日です。JUnit 5対応モジュールは提供されていません。

@PrepareForTestに指定するクラスの決め方

静的メソッドを定義しているクラスの指定

PowerMockは、指定されたクラスを専用のクラスローダで読み込み直し、バイトコードを書き換えることで静的メソッドやfinalメソッドを差し替えます。したがって書き換えたい定義そのものを持つクラスが指定対象です。TaxTable.rateOf()という静的メソッドをモック化したいなら、@PrepareForTest(TaxTable.class)と書きます。複数クラスをまとめて準備する場合は@PrepareForTest({TaxTable.class, RateLoader.class})のように配列で渡します。

モックしたい対象 @PrepareForTestに書くクラス
通常の静的メソッド そのメソッドを定義しているクラス
finalメソッド・finalクラス そのメソッドを定義しているクラス
privateメソッド そのメソッドを持つテスト対象クラス
システムクラス(java.*) それを呼び出す側のクラス
コンストラクタ(whenNew) newを実行する側のクラス
@RunWith(PowerMockRunner.class)
@PrepareForTest(TaxTable.class)
public class InvoiceServiceTest {

    @Test
    public void 軽減税率の対象なら8パーセントで計算する() {
        PowerMockito.mockStatic(TaxTable.class);
        Mockito.when(TaxTable.rateOf("FOOD")).thenReturn(8);

        InvoiceService service = new InvoiceService();
        Assert.assertEquals(1080, service.totalOf("FOOD", 1000));
    }
}

システムクラスとwhenNewで指定対象が入れ替わる条件

公式wikiがこの2ケースだけ別ページを立てて例外扱いしており、指定対象が反転するのはここだけです。システムクラスについてwikiは「通常はモックしたい静的メソッドを含むクラス(Xとする)を準備するが、PowerMockがシステムクラスをテスト用に準備することは不可能なので別の方法を取る。Xを準備する代わりにXの静的メソッドを呼び出しているクラスを準備する」と説明しています。URLEncoder.encode()やSystem.currentTimeMillis()をモック化するなら、指定するのはURLEncoder.classではなく、それを呼んでいる自分のクラスです。

コンストラクタのモック(whenNew)も同じ向きになります。wikiはnew File(..)の例で「File.classを準備しないのは、expectNewのときFileの新しいインスタンスが決して生成されず、PowerMockはnewを実行するクラスからのnew呼び出しを横取りするだけだからだ」と述べています。newを書いた側のクラスを指定してください。

@RunWith(PowerMockRunner.class)
@PrepareForTest(ReportWriter.class)
public class ReportWriterTest {

    @Test
    public void 出力先ファイルの生成を差し替える() throws Exception {
        File dummy = Mockito.mock(File.class);
        PowerMockito.whenNew(File.class).withArguments("report.csv").thenReturn(dummy);

        new ReportWriter().write("report.csv");
        Mockito.verify(dummy).createNewFile();
    }
}

withArgumentsには、ReportWriterが実際にnew File(...)へ渡す引数をそのまま書きます。ここを@PrepareForTest(File.class)に変えると、whenNewが効かずに実ファイルが作られたうえ、verifyが「Wanted but not invoked」で落ちます。原因がモックの指定ミスだと気づきにくい落ち方なので、モックが効かないときは指定したクラスが「定義側」か「呼び出し側」かを先に確認してください。

PowerMockRunnerと依存定義の設定手順

Maven/Gradleで揃える依存とMockitoの上限

PowerMockitoは単体では動きません。JUnit 4と結合するモジュール、Mockitoへのブリッジ、Mockito本体、JUnit 4本体の4つを揃えます。注意すべきはMockito側の上限です。powermock-2.0.9タグのbuild.gradleはmockitoVersion = "3.3.3"とjunitVersion = "4.12"を宣言しており、公式wikiの対応表も「Mockito 2.8.9以降=PowerMock 2.x」という行で止まっています。Mockito 4系・5系との対応は公式に示されていないため、3.x系までに固定するのが安全です。

<dependencies>
  <dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.3.3</version>
    <scope>test</scope>
  </dependency>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
  </dependency>
</dependencies>
testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
testImplementation 'org.mockito:mockito-core:3.3.3'
testImplementation 'junit:junit:4.13.2'

テストクラス側は@RunWith(PowerMockRunner.class)を付けるだけです。JUnit本体の書き方はJUnitの基本と書き方をまとめた記事で確認できます。

@PowerMockRunnerDelegateによる他Runnerとの併用

JUnit 4のRunnerはクラスに1つしか付けられません。SpringJUnit4ClassRunnerやParameterizedを使っているテストにPowerMockを足すと、ここで衝突します。解決策は、PowerMockRunnerを外側に置き、本来のRunnerを@PowerMockRunnerDelegateに委譲する形です。

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class)
@PrepareForTest(TaxTable.class)
public class InvoiceServiceSpringTest {
}

静的メソッドのMock化と呼び出し検証の書き方

mockStatic後の既定値とスタブ定義に使うwhen

PowerMockito.mockStatic(X.class)を呼んだ時点で、Xの静的メソッドはすべて既定値(オブジェクトはnull、intは0)を返すようになります。スタブしていない静的メソッドがnullを返してNullPointerExceptionになるのは、この挙動が原因です。

戻り値の指定には通常のMockito.whenを使います。PowerMockitoにも同じシグネチャのwhen(T methodCall)がありますが、こちらの本来の役目はwhen(Object instance, String methodName, Object... arguments)のようなprivateメソッド向けオーバーロードの提供です。静的メソッドのスタブでどちらを書くか迷ったら、Mockito側に寄せておけば読み手の負担が減ります。モックとスタブの役割の違いはモックとスタブの違いとテストダブルの5分類を解説した記事で整理しています。

verifyStaticの2行イディオム

PowerMock 2系のverifyStaticは、引数に検証対象のメソッド呼び出しを渡す形式ではありません。「verifyStaticでクラスを指定する行」と「検証したい静的メソッドを書く行」の2行で1組です。1行にまとめて書くと、検証がまったく行われないまま成功します。

PowerMockito.verifyStatic(TaxTable.class, Mockito.times(1));
TaxTable.rateOf("FOOD");

2行目は実処理として走るのではなく、検証したいメソッドの指定として解釈されます。回数指定を省いたverifyStatic(TaxTable.class)は、ソース上もverifyStatic(mockedClass, times(1))を呼ぶだけのエイリアスです。なお1.7系で非推奨だった引数なしのverifyStatic()は2.0で削除され、2.xのPowerMockitoにはClassを取る2つのオーバーロードしか残っていません。旧コードを持ち込むとコンパイルが通らない点に注意してください。

PowerMockのバージョン互換とメンテナンス状況

最終リリース2.0.9とリリースブランチの停止時期

依存を固定する前に、プロジェクトの現状を数字で押さえておく価値があります。GitHubのリリース情報では、PowerMockの最新リリースは2.0.9で公開日は2020年11月2日です。既定ブランチrelease/2.xの最終コミットは2022年2月24日で、内容はMockito 4.3.1との互換を取るプルリクエスト(#1113)でした。この変更はリリースされていないため、公開済みのアーティファクトはMockito 4系に対応していません。リポジトリはアーカイブされていないものの、5年以上リリースが出ていない状態が続いています。

新しいJDKへの追随も止まっています。powermock-2.0.9タグのbuild.gradleが宣言するバイトコード操作ライブラリはjavassist 3.27.0-GA・byte-buddy 1.10.14・objenesis 3.0.1で、いずれもJDK 17の一般提供(2021年9月)より前の版に固定されています。JDKを上げた直後にテストだけが落ちる場合、まずこの固定を疑ってください。

JUnit 5対応が入っていない経緯

PowerMockにJUnit 5用のモジュールはありません。公式リポジトリではSupport jUnit5というissue(#830)が2017年8月7日に作成され、現在もopenのままです。同趣旨のissueは2018年(#929)、2022年(#1129)にも立っています。JUnit 5へ移行するプロジェクトでPowerMockを残す選択肢は事実上ないため、テストをJUnit 4に据え置くか、静的メソッドのモックをMockito側へ寄せるかの二択になります。

Mockito 5系ではPowerMockを併用できない条件

Mockito側は、3.4.0(2020年7月10日)のリリースノートに「Enable mocking static methods in Mockito」と記載し、静的メソッドのモックを自前で提供しています。5.0.0(2023年1月14日)ではinline mock makerが既定になり、最低要件はJava 11以上へ引き上げられました。従来のsubclass mock makerはmockito-subclassという別アーティファクトに分離されています。最新は5.23.0(2026年3月11日リリース)です。

公開済みのPowerMock 2.0.9はmockito-core 3.3.3を前提にビルドされているため、Mockito 5系を採用した時点でPowerMockとの併用は成立しません。「PowerMockを足すかどうか」ではなく、静的メソッドは標準のmockStaticへ寄せる、privateメソッドへの依存が残るならMockito 3.x系に据え置いてテスト設計を見直す、という二択で考えます。判断の分かれ目はMockitoネイティブへの移行判断をまとめた記事で条件別に整理しています。

よくある質問

@PrepareForTestには複数のクラスを指定できますか?

指定できます。公式wikiのシステムクラスの例も@PrepareForTest( { SystemClassUser.class })と波かっこで書かれており、値は配列です。ただし準備したクラスは専用クラスローダで読み直されるため、必要なクラスだけに絞るほうがテストは安定します。

PowerMockRule(@Rule)はRunnerの代わりに使えますか?

Runnerを他のフレームワークに占有されていて@PowerMockRunnerDelegateも使えない場合の選択肢になります。powermock-module-junit4-ruleに加えてpowermock-classloading-xstreamまたはpowermock-classloading-objenesisの追加が必要で、クラスローダの切り替え方もRunner方式と変わります。Runnerで動く構成ならRunnerのままにしておくほうがトラブルは少なくなります。

JaCoCoでカバレッジが計測されなくなるのはなぜですか?

@PrepareForTestの対象クラスがPowerMock独自のクラスローダで読み直され、JaCoCoエージェントによる実行時のクラス書き換えが反映されないためです。対象クラスのカバレッジが0%と表示されることがあります。JaCoCo公式は、オフラインインストルメンテーションが必要になるケースとして「動的にクラスファイルを変換する他のエージェントとの衝突」を挙げており、回避策はオフラインインストルメンテーションへの切り替えか、対象クラスを準備対象から外すことです。テスト網羅の指標そのものはバグ密度・テスト密度の目安を扱った記事を参照してください。

Mockitoのwhenだけでは静的メソッドをモック化できないのはなぜですか?

Mockitoのwhenはモックインスタンスへの呼び出しを記録する仕組みで、インスタンスを介さない静的メソッドは記録対象になりません。静的メソッドを扱うには、PowerMockitoのmockStaticか、Mockito 3.4.0以降のMockito.mockStaticが返すスコープ付きモックが必要です。

「モック化」はスタブとどう違いますか?

スタブは決められた値を返すだけの代役、モックは呼び出され方まで検証できる代役という区別が一般的です。PowerMockitoのmockStaticとverifyStaticは、この「呼び出され方の検証」までを静的メソッドに対して行うためのAPIにあたります。分類の詳細はテストダブルの5分類を解説した記事にまとめてあります。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.15 テックブログ リゾートトラスト不正アクセス:会員2.6万件・カハラ横浜3.6万件の対象確認と窓口
  2. 2026.10.16 テックブログ バーガーキング不正アクセスで約1,118万件の情報漏洩|対象確認・案内メールの真偽と運営側のログ点検
  3. 2026.10.15 テックブログ 銀のさら不正アクセス:最大650万件の対象確認と窓口、パスワード変更の手順
  4. 2026.10.16 テックブログ ソースネクスト不正アクセスで最大2,252万件流出|ログインできない理由・パスワード再設定と会員APIの点検
  5. 2026.01.20 テックブログ PineDramaとは?TikTokのショートドラマアプリの日本配信・料金・安全性を解説

RELATED POSTS 関連記事

目次