用jvm参数javaagent的方式使用Mockito(以后的jdk将不支持动态agent加载)

75 阅读2分钟

在jdk21+版本中直接通过依赖库的方式使用Mockito(Mockito-inline)时会弹出警告

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/fqy/.gradle/caches/modules-2/files-2.1/net.bytebuddy/byte-buddy-agent/1.17.7/fbf3d6d649ed37fc9e9c59480a05be0a26e3c2da/byte-buddy-agent-1.17.7.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

即在以后的jdk发行版本中会移除动态agnet加载的支持!

此时需要通过javaagent的方式来使用mockito,使用方式如下:

Mockito - mockito-core latest javadoc - Explicitly setting up instrumentation for inline mocking (Java 21+)

在gradle中

配置

  1. 添加一个自定义配置mockitoAgent
    • configurations { mockitoAgent }
  2. 在dependencies块中向mockitoAgent配置中添加Mockito核心依赖
    • mockitoAgent(libs.mockito) { transitive = false }
  3. 在test块中读取到Mockito的jar绝对路径并作为jvm参数
    • jvmArgs += "-javaagent:${configurations.mockitoAgent.asPath}"

详细build.gradle配置如下:

plugins {
    id 'groovy'
    id 'java'
}

group = 'local.my'
version = '1.0-SNAPSHOT'

sourceSets {
    main {
        groovy {
            srcDirs = ['src/main/groovy', 'src/main/java']
        }
    }
    test {
        groovy {
            srcDirs = ['src/test/groovy', 'src/test/java']
        }
    }
}

repositories {
    mavenCentral()
}

configurations {
    /**创建一个名为mockitoAgent的自定义配置;
     * 这个配置类似于implementation、testImplementation等,但是用户自定义的;
     * 可以在构建脚本中单独引用这个配置中的依赖;*/
    mockitoAgent
}

dependencies {
    implementation 'org.apache.groovy:groovy:5.0.2'
    testImplementation platform('org.junit:junit-bom:5.13.4')
    testImplementation 'org.junit.jupiter:junit-jupiter'
    testImplementation 'org.mockito:mockito-junit-jupiter:5.20.0'

    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    //向mockitoAgent配置中添加Mockito核心依赖,并禁用传递依赖
    mockitoAgent('org.mockito:mockito-core:5.20.0') { transitive = false }
}

tasks.withType(GroovyCompile).configureEach {
    // 启用groovy的增量编译
    options.incremental = true
    options.incrementalAfterFailure = true
}
tasks.withType(JavaCompile).configureEach {
    // 指定java版本
    options.release = 25
}


test {
    useJUnitPlatform()
    // 配置运行测试用例时的JVM参数添加mockito javaagent
    jvmArgs += "-javaagent:${configurations.mockitoAgent.asPath}"
    println '>>>gradle版本: '+gradle.gradleVersion
    println '>>>运行测试用例时的jvm参数: '+jvmArgs
    // 测试输出配置
    testLogging {
        events "passed", "skipped", "failed", "standardOut", "standardError"
        // 显示System.out和System.err的输出
        showStandardStreams = true
        showCauses = true
        showExceptions = true
        showStackTraces = true
        exceptionFormat = 'full'
    }
    // 在控制台实时显示输出
    outputs.upToDateWhen { false }
}

测试

首先准备一个被测试类Main.groovy

package local.my.demo_jdk


static String say(){
    return 'Hello world!'
}

然后创建一个测试类Gv1Test.groovy

package local.my.demo_jdk.t

import local.my.demo_jdk.Main
import org.junit.jupiter.api.Test
import static org.mockito.Mockito.*


class Gv1Test {
    @Test
    void test1(){
        println '开始测试-----'
        println 'groovy version : '+GroovySystem.version
        println 'before mock say'
        println Main.say()
        println 'after mock say'
        try (def _ = mockStatic(Main).tap {
            it.when {Main.say()}.thenReturn("mock say")
        }){
            println Main.say()
        }
        println '结束测试-----'
    }
}

最后运行测试./gradlew test

输出日志如下:

> Configure project :
>>>gradle版本: 9.2.0
>>>运行测试用例时的jvm参数: [-javaagent:/Users/fqy/.m2/repository/org/mockito/mockito-core/5.20.0/mockito-core-5.20.0.jar]

> Task :test

Gv1Test > test1() STANDARD_OUT
    开始测试-----
    groovy version : 5.0.2
    before mock say
    Hello world!
    after mock say
    mock say
    结束测试-----

Gv1Test > test1() PASSED

BUILD SUCCESSFUL in 1s

在maven中

  1. pom.xmlbuild块的maven-surefire-plugin插件添加argLine配置@{argLine} -javaagent:${org.mockito:mockito-core:jar}
  2. pom.xmlbuild块的maven-dependency-plugin插件添加goal配置properties

完整的pom.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>local.my</groupId>
    <artifactId>demo_jdk_new</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>25</maven.compiler.source>
        <maven.compiler.target>25</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>5.20.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>properties</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <argLine>@{argLine} -javaagent:${org.mockito:mockito-core:jar}</argLine>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

或者直接通过jvm参数-XX:+EnableDynamicAgentLoading在运行测试时允许动态加载agent

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <argLine>@{argLine} -XX:+EnableDynamicAgentLoading</argLine>
            </configuration>
        </plugin>
    </plugins>
</build>