Spring Boot のテストで発生する Mockito の警告を解決する方法

Spring Boot のプロジェクトを作成し、テストを実行した際、Mockito に関する警告が表示される問題について、解決方法を備忘録として残します。

# 前提
以下の設定で Spring Boot プロジェクトを作成します。
- [Spring Initializr](https://start.spring.io/) を使用
```
Project： Gradle - Kotlin
Language： Kotlin
Spring Boot： 3.4.3

Project Metadata
  設定変更なし

Packaging： Jar
Java： 21

Dependencies： なし
```

<details><summary>Spring Initializr 設定画面</summary>

![spring-initializr-20250320.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3695365/39df1d36-3a27-4089-9d1c-4c8c41b4e110.png)

</details>

# 発生する問題
`DemoApplicationTests` を実行すると、次のような警告が表示されます。

```shell
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 what is described in Mockito's documentation: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.3
WARNING: A Java agent has been loaded dynamically (/Users/user/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.15.11/a38b16385e867f59a641330f0362ebe742788ed8/byte-buddy-agent-1.15.11.jar)
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
WARNING: Dynamic loading of agents will be disallowed by default in a future release
```

この警告は、Mockito が Java のエージェントを動的にロードしていることに起因します。
今後の JDK のバージョンではこの方法が制限されるため、適切な設定を行う必要があります。

<details><summary><b>警告の詳細</b></summary>

## **警告の詳細**
### **1. Mockito が Java エージェントを動的にロード**
```
Mockito is currently self-attaching to enable the inline-mock-maker.
```
- Mockito は **"inline mock-maker"** という機能を使用してモックを作成します。
- これを有効にするために、Mockito は **Java エージェント（`byte-buddy-agent`）を動的にアタッチ**しています。

### **2. 将来的にこの方法が使用できなくなる**
```
This will no longer work in future releases of the JDK.
```
- **現在は動作するが、将来の JDK では禁止される予定**という警告です。
- **Java のセキュリティポリシーが変更され、動的エージェントのロードが制限される可能性**があります。

### **3. `byte-buddy-agent` がロードされた**
```
A Java agent has been loaded dynamically (/Users/user/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.15.11/a38b16385e867f59a641330f0362ebe742788ed8/byte-buddy-agent-1.15.11.jar)
```
- `byte-buddy-agent` は、Mockito がモックオブジェクトを作成するために内部的に使用するエージェントです。
- **Mockito の "inline mock-maker" 機能を利用すると、自動的にこのエージェントがロードされます。**

### **4. エージェントのロードに関する追加の警告**
```
WARNING: If a serviceability tool is in use, please run with -XX:+EnableDynamicAgentLoading to hide this warning
```
- **`-XX:+EnableDynamicAgentLoading` を指定すれば、この警告を非表示にできる**という提案です。
- **"serviceability tool"**（例：JVMのデバッグツール）を使用している場合、このオプションを付けるとエージェントのロードを許可できます。

```
WARNING: If a serviceability tool is not in use, please run with -Djdk.instrument.traceUsage for more information
```
- **`-Djdk.instrument.traceUsage`** を付けると、エージェントがどのように使用されているのか詳しい情報を取得できます。

```
WARNING: Dynamic loading of agents will be disallowed by default in a future release
```
- **JDK の将来のバージョンで、動的に Java エージェントをロードする機能がデフォルトで禁止される**可能性があります。

</details>

# 解決方法
`build.gradle.kts` に以下の設定を追加し、Mockito のエージェントを明示的に指定します。

```kotlin
val mockitoAgent: Configuration by configurations.creating

dependencies {
    mockitoAgent("net.bytebuddy:byte-buddy-agent:1.17.2") { isTransitive = false }   // 最新バージョンを指定
}

tasks.withType<Test> {
    jvmArgs("-javaagent:${mockitoAgent.singleFile.absolutePath}")
}
```

<details><summary>build.gradle.kts 全体</summary>

```diff_kotlin
+ val mockitoAgent: Configuration by configurations.creating
  
  plugins {
      kotlin("jvm") version "1.9.25"
      kotlin("plugin.spring") version "1.9.25"
      id("org.springframework.boot") version "3.4.3"
      id("io.spring.dependency-management") version "1.1.7"
  }
  
  group = "com.example"
  version = "0.0.1-SNAPSHOT"
  
  java {
      toolchain {
          languageVersion = JavaLanguageVersion.of(21)
      }
  }
  
  repositories {
      mavenCentral()
  }
  
  dependencies {
      implementation("org.springframework.boot:spring-boot-starter")
      implementation("org.jetbrains.kotlin:kotlin-reflect")
      testImplementation("org.springframework.boot:spring-boot-starter-test")
      testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
      testRuntimeOnly("org.junit.platform:junit-platform-launcher")
+     mockitoAgent("net.bytebuddy:byte-buddy-agent:1.17.2") { isTransitive = false }   // 最新バージョンを指定
  }
  
  kotlin {
      compilerOptions {
          freeCompilerArgs.addAll("-Xjsr305=strict")
      }
  }
  
  tasks.withType<Test> {
      useJUnitPlatform()
+     jvmArgs("-javaagent:${mockitoAgent.singleFile.absolutePath}")
  }
```

</details>

<details><summary><b>追加した設定の詳細</b></summary>

## **追加した設定の詳細**
### **1. `mockitoAgent` の作成**
```kotlin
val mockitoAgent: Configuration by configurations.creating
```
- **`mockitoAgent` という新しい Gradle の依存関係設定（`Configuration`）を作成**しています。
- これは、Mockito の `byte-buddy-agent` を**テスト実行時に Java エージェントとして適用するための準備**です。

---

### **2. `mockitoAgent` に `byte-buddy-agent` を追加**
```kotlin
dependencies {
    // (省略)
    mockitoAgent("net.bytebuddy:byte-buddy-agent:1.17.2") { isTransitive = false }
}
```
- `mockitoAgent` に **`byte-buddy-agent`（Mockito がモックを作成するために必要なライブラリ）を追加**します。
- 最新バージョンは [MVN REPOSITORY](https://mvnrepository.com/artifact/net.bytebuddy/byte-buddy-agent) から確認できます。
- **`isTransitive = false` に設定**
  - これは**依存関係の伝播（transitive dependency）を防ぐ設定**です。
  - `byte-buddy-agent` 自体には**追加の依存関係が不要** なので、これを `false` にすることで**不要なライブラリをインストールしない**ようにします。

---

### **3. テスト実行時に `-javaagent` を追加**
```kotlin
tasks.withType<Test> {
    useJUnitPlatform()
    jvmArgs("-javaagent:${mockitoAgent.singleFile.absolutePath}")
}
```
- **テスト実行時に `-javaagent` オプションを JVM に追加**します。
- `mockitoAgent.singleFile.absolutePath` を指定することで、**ダウンロードした `byte-buddy-agent` を Java エージェントとしてロード**します。
- これにより、Mockito は**自動でエージェントを動的にロードする必要がなくなる**ため、警告が消えます。



## **変更後の動作**
**Before（変更前）**
- Mockito は**動的に Java エージェントをロードしようとする**。
- しかし、将来的な JDK の変更で**この方法が禁止される可能性**があるため、警告が表示される。

**After（変更後）**
- **事前に `byte-buddy-agent` をダウンロードし、Java エージェントとして明示的に指定する**。
- **Mockito が自動でエージェントをロードしようとする必要がなくなる**ため、警告が消える。

</details>

この設定により、Mockito のエージェントが正しく適用され、警告が解消されます。

# 動作確認
再度 `DemoApplicationTests` を実行し、警告が表示されなくなったことを確認します。

# おわりに
WARNING の中に出てくる [URL](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.3) を参考にしようと開いてみたのですが、`Please add Mockito as an agent to your build what is described in Mockito's documentation` と書いているわりに、具体的な手順が記載されているところが見つけられませんでした。私の見方が悪いだけかもしれませんが、もし分かる方がいましたら教えていただけると助かります。

# 参考
- [Support configuring Mockito java agent in java 21+  #1590](https://github.com/spring-io/initializr/issues/1590)