When working with recent versions of Java (21+) and Mockito, many developers encounter a puzzling warning message during test execution:
1 2 3 4 5 6 |
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: https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3 WARNING: A Java agent has been loaded dynamically (/Users/duke/.m2/repository/net/bytebuddy/byte-buddy-agent/1.17.5/byte-buddy-agent-1.17.5.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 OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended |
This warning appears when Mockito attempts to dynamically load its inline mock maker, a mechanism that enables advanced mocking capabilities. Understanding this warning and addressing it properly ensures our Spring Boot test suites remain reliable and future-proof.
Why This Warning Appears
The warning stems from changes in Java’s security model regarding dynamic agent loading.
Mockito’s inline mock maker requires bytecode manipulation to create mocks of final classes and static methods.
Historically, Mockito achieved this through ByteBuddy’s dynamic agent attachment, which allowed runtime modification of the JVM without explicit configuration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @Test void shouldCreateUser() { // This test triggers Mockito's inline mock maker // which attempts dynamic agent loading when(userRepository.save(any(User.class))).thenReturn(user); } } |
Starting with Java 21, and increasingly restrictive in earlier versions, the JVM warns about dynamic agent loading and plans to disallow it entirely in future releases. This security enhancement prevents malicious code from modifying JVM behavior at runtime, but it affects legitimate tools like Mockito that rely on this capability.
The warning indicates that Mockito successfully attached its agent dynamically, but this mechanism will become unavailable in future Java versions. We need to explicitly configure the Mockito agent to ensure our tests continue working.
Understanding the Warning Components
The warning message contains several important pieces of information:
The first part explains that Mockito is self-attaching its inline mock maker and directs us to configure it as a proper Java agent. The subsequent warnings about dynamic agent loading come from the JVM itself, indicating that ByteBuddy (Mockito’s underlying bytecode manipulation library) loaded an agent dynamically.
The EnableDynamicAgentLoading
flag mentioned in the warning is a temporary workaround that suppresses the warning without solving the underlying issue.
While this might seem like a quick fix, it doesn’t address the fundamental problem and won’t work when dynamic agent loading is completely removed.
Configuring Maven for Proper Agent Loading
For Maven projects, we need to configure both the Surefire plugin (for unit tests) and the Failsafe plugin (for integration tests) to load the Mockito agent explicitly. This approach ensures our tests work consistently across different environments and Java versions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.1.2</version> <configuration> <argLine> -javaagent:${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar </argLine> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>3.1.2</version> <configuration> <argLine> -javaagent:${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar </argLine> </configuration> </plugin> </plugins> </build> <properties> <mockito.version>5.4.0</mockito.version> </properties> |
This configuration tells Maven to load the Mockito JAR as a Java agent before running tests. The agent path uses Maven’s local repository location, ensuring the correct version is loaded based on our project dependencies.
For Spring Boot projects using the spring-boot-maven-plugin, we might need to handle the argLine configuration more carefully if we’re already using JaCoCo or other agents:
1 2 3 4 5 6 7 8 |
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.1.2</version> <configuration> <argLine>@{argLine} -javaagent:${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar</argLine> </configuration> </plugin> |
The @{argLine}
placeholder allows other plugins like JaCoCo to prepend their own agent configurations.
Configuring Gradle for Proper Agent Loading
Gradle projects require similar configuration in the test tasks. We can configure this globally for all test tasks or specifically for different test types:
1 2 3 4 5 6 7 8 9 10 |
test { jvmArgs "-javaagent:${configurations.testRuntimeClasspath.find { it.name.contains('mockito-core') }.absolutePath}" } // For integration tests if using a separate task task integrationTest(type: Test) { jvmArgs "-javaagent:${configurations.testRuntimeClasspath.find { it.name.contains('mockito-core') }.absolutePath}" testClassesDirs = sourceSets.integrationTest.output.classesDirs classpath = sourceSets.integrationTest.runtimeClasspath } |
A more robust Gradle configuration that handles multiple test tasks might look like this:
1 2 3 4 5 6 7 8 9 10 |
subprojects { tasks.withType(Test) { def mockitoAgent = configurations.testRuntimeClasspath.find { it.name.contains('mockito-core') } if (mockitoAgent) { jvmArgs "-javaagent:${mockitoAgent.absolutePath}" } } } |
This approach automatically configures the Mockito agent for all test tasks across all subprojects, making it easier to maintain in multi-module projects.
Configuring IntelliJ IDEA
IntelliJ IDEA requires configuration in the run/debug configurations to properly load the Mockito agent.
We can set this up globally for all tests or individually for specific test configurations.
To configure globally, navigate to Run/Debug Configurations → Edit Configurations → Templates → JUnit. In the VM options field, add:
1 |
-javaagent:/path/to/.m2/repository/org/mockito/mockito-core/5.4.0/mockito-core-5.4.0.jar |
Replace the path with your actual Maven local repository path and the appropriate Mockito version. On macOS and Linux, this typically looks like:
1 |
-javaagent:${user.home}/.m2/repository/org/mockito/mockito-core/5.4.0/mockito-core-5.4.0.jar |
For Windows:
1 |
-javaagent:%USERPROFILE%\.m2\repository\org\mockito\mockito-core\5.4.0\mockito-core-5.4.0.jar |
Alternatively, we can create a custom JVM configuration by adding a .idea/runConfigurations
file or modifying the project’s run configuration templates. This approach ensures that all team members have consistent test execution settings.
For Gradle projects in IntelliJ, we can leverage Gradle’s test configuration by ensuring IntelliJ delegates test execution to Gradle.
In IntelliJ settings, navigate to Build, Execution, Deployment → Build Tools → Gradle, and set “Run tests using” to “Gradle (Default)”.
Best Practices and Considerations
When implementing these configurations, consider these best practices for maintaining reliable test environments:
First, ensure version consistency across all configuration files. When updating Mockito versions, remember to update the agent path in all relevant configuration files. Consider using properties or variables to centralize version management.
Second, be mindful of existing JVM arguments. If we’re already using other Java agents (like JaCoCo for code coverage), ensure they’re properly combined in the argLine configuration. Multiple agents can coexist, but they need to be specified correctly.
Third, test the configuration across different environments. Verify that tests work locally, in CI/CD pipelines, and on different team members’ machines. The agent loading must work consistently across all environments where tests are executed.
Finally, monitor for future Mockito updates. The Mockito team continues to improve agent loading mechanisms, so stay informed about changes in newer versions that might simplify or modify the configuration requirements.
Summary
The Mockito agent loading warning appears because modern Java versions restrict dynamic agent loading for security reasons. While this warning doesn’t break our tests immediately, addressing it properly ensures our test suites remain functional as Java continues to evolve.
By configuring Maven Surefire and Failsafe plugins, Gradle test tasks, and IntelliJ run configurations to explicitly load the Mockito agent, we eliminate the warning and future-proof our testing setup. This approach provides better control over our testing environment and aligns with Java’s enhanced security model.
Remember that this configuration is essential for projects using Mockito’s advanced features like mocking final classes and static methods. Taking the time to configure the agent properly now prevents potential issues when upgrading to newer Java versions and ensures our Spring Boot testing practices remain robust and reliable.
Joyful testing,
Philip