JDK を上げたあと、アプリケーションのコードも pom.xml のテスト依存も変えていないのに、モックを作るテストだけが落ちます。コンソールに最初に出るのは、自分が書いたクラスの名前です。
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class demo.Greeter.
If you're not sure why you're getting this error, please open an issue on GitHub.
Java : 25
JVM vendor version : 25.0.3+9-LTS
...
Underlying exception : org.mockito.exceptions.base.MockitoException: Could not modify all classes [class java.lang.Object, class demo.Greeter]
実際の原因は、この 4 段下にあります。
Caused by: java.lang.IllegalArgumentException: Java 25 (69) is not supported by the current version of Byte Buddy which officially supports Java 23 (67) - update Byte Buddy or set net.bytebuddy.experimental as a VM property
同じ検索で当たる表示ゆれは次のとおりです。
Mockito cannot mock this class: class <あなたのクラス>(先頭に出る行。クラス名だけが違う)Could not modify all classes [class java.lang.Object, class <あなたのクラス>]Byte Buddy could not instrument all classes within the mock's type hierarchyJava 24 (68) is not supported by the current version of Byte Buddy which officially supports Java 23 (67)(JDK 24 で走らせた場合。数字だけが変わります)- update Byte Buddy or set net.bytebuddy.experimental as a VM property(同じ行の末尾)
修正は、mockito-core を上げることです。 demo.Greeter にも、テストコードにも手を入れる必要はありません。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.23.0</version>
<scope>test</scope>
</dependency>
Gradle なら次のとおりです。
testImplementation("org.mockito:mockito-core:5.23.0")
net.bytebuddy の依存は書き足しません。mockito-core が推移的に引くので、mockito-core を上げると一緒に上がります。
なぜ JDK 24 以降で出るのか:Byte Buddy が知らない class file version
Mockito 5 の既定のモック生成方式(inline mock maker)は、モック対象のクラスを実行中に書き換えます。冒頭のログに出る You are seeing this disclaimer because Mockito is configured to create inlined mocks. がその宣言です。書き換えを担当しているのが Byte Buddy で、Byte Buddy は自分が知らない class file version のクラスを渡されると、読み進めずに例外を投げます。
class file version は JDK の版に 44 を足した値です。
| JDK | class file version |
|---|---|
| 21 | 65 |
| 22 | 66 |
| 23 | 67 |
| 24 | 68 |
| 25 | 69 |
mockito-core 5.13.0 が引く Byte Buddy 1.15.0 が知っているのは 67(Java 23)までです。JDK 25 で走らせると 69 を渡されて止まります。書き換えの対象には java.lang.Object も含まれるので、自分のクラスがどれだけ単純でも避けられません(Could not modify all classes [class java.lang.Object, ...] の java.lang.Object がそれです)。
止まっているのはテスト実行時のバイトコード書き換えだけです。この再現では、同じ JDK で javac は通り、失敗するのは Mockito がモックを作るテストだけでした(mockito-core は test スコープにしか入れていません。本番でも Byte Buddy を使うライブラリを入れている場合は、下の「切り分け」を見てください)。
自分のクラスの class file version を下げても変わりません。 この再現は maven.compiler.release を 17 にして demo.Greeter を 61 でコンパイルしたうえで落ちており、21 に変えても同じところで止まります。書き換えの対象に入る java.lang.Object は実行中の JDK のもの(25 なら 69)で、release では動かせないからです。
エラーの本文が、3 段にわたって別のものを指している
この記事の実質は、原因までの距離です。例外の連鎖は 4 段あり、版の食い違いが書いてあるのは 4 段目だけで、そこへ行き着く前に 1 段目と 3 段目が別のものを名指しします。
| 段 | 本文 | 何を指しているか |
|---|---|---|
| 1 | Mockito cannot mock this class: class demo.Greeter. | 自分が書いたクラス |
| 2 | Could not modify all classes [class java.lang.Object, class demo.Greeter] | 書き換えに失敗したクラスの一覧 |
| 3 | Byte Buddy could not instrument all classes within the mock's type hierarchy + This problem should never occur for javac-compiled classes. | scalac と Android |
| 4 | Java 25 (69) is not supported by the current version of Byte Buddy ... | JDK と Byte Buddy の版 |
1 段目を読んだ読者は demo.Greeter を疑います。final が付いていないか、コンストラクタが private ではないか、と探しても、そこには何もありません。
3 段目はさらに遠くを指します。原文はこうです。
This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:
- Compiled by older versions of scalac
- Classes that are part of the Android distribution
この 3 行は、javac でコンパイルした素の Java クラスで出ています。 Scala も Android も関わっていません。「javac 由来のクラスでは起きないはず」と書かれた文が、javac 由来のクラスで出るので、ここで読むのをやめると調べる方向が固定されます。
原因が書いてあるのは 4 段目だけです。しかも mvn test の既定では、4 段目はコンソールに出ません——標準出力に出るのは 1 段目と Underlying exception の行までで、全文は target/surefire-reports/<テストクラス>.txt の側にあります。CI のログだけを見ていると 4 段目に到達しないことがあるので、そのときはレポートファイルを開くか、-Dsurefire.useFile=false を付けて実行し直します。
境界は JDK の LTS ではなく、Mockito が引く Byte Buddy の版
mockito-core 5.13.0 を固定したまま JDK だけを変えると、こうなります。
| JDK | 結果 |
|---|---|
| 21 | 成功 |
| 24 | Java 24 (68) is not supported ... |
| 25 | Java 25 (69) is not supported ... |
21 と 25 のあいだではなく、21 と 24 のあいだが境界です。 LTS から LTS へ跳ぶ人だけの問題ではなく、非 LTS を経由した時点で同じところで止まります。
JDK 25 に固定して mockito-core の版を動かすと、境界は Byte Buddy 側にあることが分かります。
| mockito-core | 推移的に引く Byte Buddy | JDK 25 | メッセージ中の「公式対応」上限 |
|---|---|---|---|
| 5.13.0 | 1.15.0 | 失敗 | Java 23 (67) |
| 5.15.2 | 1.15.11 | 失敗 | Java 24 (68) |
| 5.16.0 | 1.15.11 | 失敗 | Java 24 (68) |
| 5.17.0 | 1.15.11 | 失敗 | Java 24 (68) |
| 5.18.0 | 1.17.5 | 成功 | — |
| 5.19.0 | 1.17.6 | 成功 | — |
| 5.20.0 | 1.17.7 | 成功 | — |
| 5.23.0 | 1.17.7 | 成功 | — |
JDK 25 で最初に通るのは 5.18.0 です。「5.16 以上に上げれば直る」という案内を見かけたら、それがどの JDK の話かを確かめてください。 JDK 24 でこのエラーを踏んだ場合はその案内で直ります(5.16.0 が引く 1.15.11 は Java 24 まで知っているため、JDK 24 では成功します)。JDK 25 では足りません——5.16.0 も 5.17.0 も 1.15.11 のままなので、同じところで止まります。
**「mockito が古いと落ちる」ではありません。**落ちるかどうかは (JDK の class file version, 引いている Byte Buddy の版) の組で決まります。同じ 5.17.0 でも、JDK 24 で走らせれば通ります(1.15.11 が知っている上限が 68 だからです)。だから「LTS に固定しておけば安全」も「Mockito を一度上げたから安全」も成り立たず、JDK を上げるたびに同じ組を見ることになります。
Mockito が踏む経路(ASM でクラスを読む側)で上限が上がったのは Byte Buddy 1.17.5 で、mockito-core では 5.18.0 から引かれます。Byte Buddy の README にある対応表はこれより前の版から Java 25 を挙げているので、そちらを見て「1.17.0 で足りる」と読むと、mockito-core 経由では届きません。上の表は mockito-core が実際に引く版で測ったものです。
直し方 2:mockito-core の版を動かせないときは Byte Buddy だけを上げる
Spring Boot の BOM のように、mockito-core の版が親から管理されていて動かせないことがあります。その場合は Byte Buddy の版だけを上書きします。
このエラーを消しているのは byte-buddy の側です。byte-buddy だけを 1.17.7 にして byte-buddy-agent を 1.15.0 のままにしても、mockito-core 5.13.0 のまま JDK 25 でテストが通ります。逆に byte-buddy-agent だけを 1.17.7 にしても、同じ is not supported by the current version of Byte Buddy で止まります。class file version を検査しているのが byte-buddy に入っているクラスだからです。
それでも byte-buddy と byte-buddy-agent の 2 つとも同じ版に揃えることを勧めます。mockito-core は両方を同じ版で宣言していて、片方だけを動かすと依存の版が食い違った状態が残るためです。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.17.7</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.17.7</version>
</dependency>
</dependencies>
</dependencyManagement>
Gradle なら次のとおりです。
testImplementation("net.bytebuddy:byte-buddy:1.17.7")
testImplementation("net.bytebuddy:byte-buddy-agent:1.17.7")
mockito-core を 5.13.0 に据え置いたままこの上書きだけを入れると、JDK 25 でテストが通ります。ただしこれは依存の版を 2 箇所で管理する状態なので、mockito-core を上げられるようになったら上書きを外して 1 箇所に戻します。
net.bytebuddy.experimental はエラー文自身が案内するが、版検査を外す指定
4 段目のメッセージは、直し方を 2 つ並べています。update Byte Buddy と set net.bytebuddy.experimental as a VM property です。後者は実際に通ります。
mvn test -Dnet.bytebuddy.experimental=true
mockito-core 5.13.0 のまま JDK 25 でテストが成功します。ただし上限が上がったわけではありません。Byte Buddy 側の説明では、この指定は対応している最大の class file version を検証しないというものです。読める保証が増えるのではなく、検査そのものを外します。
**このケースで確かめたのは、指定を付けるとこのテストが通る、というところまでです。**検査を外したまま Java 25 で変わったクラスファイルの構造に当たったときにどうなるかは測っていません。
一時措置として使うなら、外す条件(mockito-core を 5.18.0 以上へ上げる)を指定と同じ場所にコメントで書いておきます。記事末尾の注意書きは読まれません。
切り分け
Unsupported class file major version 65やclass file version 61.0が出ている:こちらは Byte Buddy ではなく、ビルドツールや JVM 自身が版を拒否しています。別の記事です(Unsupported class file major version 65/UnsupportedClassVersionError)。見分け方は、例外の連鎖にnet.bytebuddyのフレームが出るかどうかです。Mockito cannot mock this classは出るが、Byte Buddyの行がどこにも無い:版の問題ではありません。finalクラス・finalメソッド・匿名クラスなど、対象そのものが書き換えられない場合にも同じ 1 段目が出ます。レポートファイルのCaused by:を最後まで読んで、4 段目に相当する行を確認してください。- JDK を上げていないのに急に出はじめた:CI のイメージやツールチェーンの側で JDK が動いていないかを見ます。ログ冒頭の
Java : <版>とJVM vendor versionが、Mockito 自身の出力に含まれています。 - Mockito ではなく別のライブラリのスタックに
net.bytebuddyが出ている:Byte Buddy を使うのは Mockito だけではありません(モックサーバやエージェント系のライブラリも引きます)。その場合は、そのライブラリ側の版を上げる話になります。上の「直し方 2」の上書きは、引いている主体が誰であっても同じ形で使えます。 Dynamic loading of agents will be disallowed by default in a future releaseという警告も一緒に出ている:inline mock maker がエージェントを動的に読み込むために出る別の警告で、この記事のエラーとは独立です。テストは通ります。
検証環境
maven:3.9-eclipse-temurin-25(digest 固定。JVM vendor version は25.0.3+9-LTS、Eclipse Adoptium)、ネットワーク許可(Maven Central からの依存取得のため)- 再現:
mockito-core5.13.0 でmvn -B test -Dsurefire.useFile=falseがis not supported by the current version of Byte Buddyを出して終了コード 1 - 修正:同じイメージ・同じテストコードのまま
mockito-coreを 5.23.0 にすると終了コード 0
再現から修正までは errfix の検証ハーネスが機械的に確認しています。JDK 21 / 24 / 25 の対照、mockito-core 5.13.0 / 5.15.2 / 5.16.0 / 5.17.0 / 5.18.0 / 5.19.0 / 5.20.0 / 5.23.0 の対照、JDK 24 で 5.16.0 と 5.17.0 が通ること、maven.compiler.release を 21 にしても JDK 25 で同じところで止まること、-Dnet.bytebuddy.experimental=true を付けると 5.13.0 のままでも終了コード 0 になること、byte-buddy だけを 1.17.7 にしても(byte-buddy-agent は 1.15.0 のまま)終了コード 0 になり、byte-buddy-agent だけを 1.17.7 にすると同じシグネチャで止まることは、いずれも同じテストコードを条件だけ変えて実行して確かめています。各版が引く Byte Buddy の版は、実行時のクラスパスに載った byte-buddy-agent-<版>.jar の名前と Maven Central の POM から読みました。net.bytebuddy.experimental の意味(版検査を外す指定であること)は Byte Buddy 側の説明によります。修正として書いた mockito-core 5.23.0 は、このケースで終了コード 0 を確認した版です。
**-Dnet.bytebuddy.experimental=true を付けた状態で「別の失敗になる」ところまでは実測していません。**このケースで測ったのは、指定を付けるとこのテストが通る、というところまでです。指定の意味(版検査を外すもので、上限が上がるわけではないこと)は Byte Buddy 側の説明によります。