使用Mockito在JUnit 5中模拟静态方法

28
我正在尝试为Junit 5测试用例模拟静态类(org.apache.commons.beanutils.Beanutils)。 我遇到了mockito-inline依赖项,可以帮助模拟静态类。 我尝试在项目中使用mockito-inline,但由于某种奇怪的原因,在没有mockito-core库的情况下,它会给我编译错误。
使用mockito-core,我得到以下结果:
org.mockito.exceptions.base.MockitoException: 
The used MockMaker PowerMockMaker does not support the creation of static mocks

Mockito's inline mock maker supports static mocks based on the Instrumentation API.
You can simply enable this mock mode, by placing the 'mockito-inline' artifact where you are currently using 'mockito-core'.
Note that Mockito's inline mock maker is not supported on Android.
    at com.xx.xx.xx.AvroCopyPropertiesInvocationTargetExceptionScenario(CreditOfferServiceTest.java:1197)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137)
    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)

没有核心编译问题,请查看没有mockito-core的测试依赖项分解:

[INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.9:test
[INFO] |  \- org.powermock:powermock-api-support:jar:2.0.9:test
[INFO] |     +- org.powermock:powermock-reflect:jar:2.0.9:test
[INFO] |     \- org.powermock:powermock-core:jar:2.0.9:test
[INFO] +- org.mockito:mockito-inline:jar:3.6.28:test
[INFO] |  \- org.mockito:mockito-core:jar:3.1.0:test
[INFO] |     +- net.bytebuddy:byte-buddy:jar:1.10.10:test
[INFO] |     +- net.bytebuddy:byte-buddy-agent:jar:1.10.10:test
[INFO] |     \- org.objenesis:objenesis:jar:2.6:test
[INFO] +- org.mockito:mockito-junit-jupiter:jar:3.6.0:test
[INFO] |  \- org.junit.jupiter:junit-jupiter-api:jar:5.5.2:test
[INFO] |     +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] |     \- org.junit.platform:junit-platform-commons:jar:1.5.2


请查看此链接,看看它是否对您有所帮助:https://frontbackend.com/java/how-to-mock-static-methods-with-mockito - Vitor Cavalcanti
嗨,维托尔,它使用junit 4,我需要使用junit 5的东西。 - Arun Senthoor Pandian
2
我的意见是......首先,抛弃powermock的依赖。然后使用mockito-inline和JUnit 5(junit-jupiter),这样你就可以让事情顺利地进行。 - Vitor Cavalcanti
你能否编辑你的问题,添加你的 pom.xml 和测试类?我会帮你解决。 - Vitor Cavalcanti
嗨Vitor,感谢你的时间。在mockito-core具备模拟静态和final类的能力之前,我们决定使用Junit 4来运行它们。我看到他们正在尝试使用mockito-inline进行实验,一旦满意,他们应该会发布一个带有mockito-core的版本。那应该可以解决我们的问题。 - Arun Senthoor Pandian
6个回答

20

你需要使用mockito-inline版本3.4.0或更高版本,并从依赖中删除mockito-core(因为mockito-inline依赖于mockito-core,所以它会被自动检索)。

这样,你将正确地使用Mockito静态模拟,但它不能解决你发布的异常。

为了修复它,你需要删除所有powermock的依赖项,因为它与mockito冲突并导致此错误。

The used MockMaker PowerMockMaker does not support the creation of static mocks

13

在我的情况下,我们使用的是Spring Boot框架,并且以下依赖项spring-boot-starter-test和mockito-core是由Spring Boot框架内部提供的。

testImplementation 'org.springframework.boot:spring-boot-starter-test'

使用Gradle依赖项来查找此内容。
+--- org.springframework.boot:spring-boot-starter-test -> 2.6.6
|    +--- org.springframework.boot:spring-boot-starter:2.6.6 (*)
|    +--- org.springframework.boot:spring-boot-test:2.6.6
|    |    \--- org.springframework.boot:spring-boot:2.6.6 (*)
|    +--- org.springframework.boot:spring-boot-test-autoconfigure:2.6.6
|    |    +--- org.springframework.boot:spring-boot:2.6.6 (*)
|    |    +--- org.springframework.boot:spring-boot-test:2.6.6 (*)
|    |    \--- org.springframework.boot:spring-boot-autoconfigure:2.6.6 (*)
|    +--- com.jayway.jsonpath:json-path:2.6.0
|    |    +--- net.minidev:json-smart:2.4.7 -> 2.4.8
|    |    |    \--- net.minidev:accessors-smart:2.4.8
|    |    |         \--- org.ow2.asm:asm:9.1 -> 9.3
|    |    \--- org.slf4j:slf4j-api:1.7.30 -> 1.7.36
|    +--- jakarta.xml.bind:jakarta.xml.bind-api:2.3.3
|    |    \--- jakarta.activation:jakarta.activation-api:1.2.2
|    +--- org.assertj:assertj-core:3.21.0
|    +--- org.hamcrest:hamcrest:2.2
|    +--- org.junit.jupiter:junit-jupiter:5.8.2
|    |    +--- org.junit:junit-bom:5.8.2
|    |    |    +--- org.junit.jupiter:junit-jupiter:5.8.2 (c)
|    |    |    +--- org.junit.jupiter:junit-jupiter-api:5.8.2 (c)
|    |    |    +--- org.junit.jupiter:junit-jupiter-params:5.8.2 (c)
|    |    |    \--- org.junit.platform:junit-platform-commons:1.8.2 (c)
|    |    +--- org.junit.jupiter:junit-jupiter-api:5.8.2
|    |    |    +--- org.junit:junit-bom:5.8.2 (*)
|    |    |    +--- org.opentest4j:opentest4j:1.2.0
|    |    |    +--- org.junit.platform:junit-platform-commons:1.8.2
|    |    |    |    +--- org.junit:junit-bom:5.8.2 (*)
|    |    |    |    \--- org.apiguardian:apiguardian-api:1.1.2
|    |    |    \--- org.apiguardian:apiguardian-api:1.1.2
|    |    \--- org.junit.jupiter:junit-jupiter-params:5.8.2
|    |         +--- org.junit:junit-bom:5.8.2 (*)
|    |         +--- org.junit.jupiter:junit-jupiter-api:5.8.2 (*)
|    |         \--- org.apiguardian:apiguardian-api:1.1.2
|    +--- org.mockito:mockito-core:4.0.0
|    |    +--- net.bytebuddy:byte-buddy:1.11.19 -> 1.11.22
|    |    \--- net.bytebuddy:byte-buddy-agent:1.11.19 -> 1.11.22
|    +--- org.mockito:mockito-junit-jupiter:4.0.0
|    |    \--- org.mockito:mockito-core:4.0.0 (*)

所以,解决这个问题有两个步骤:
1. 从spring-boot-starter-test中排除mockito-core。 2. 在pom.xml文件中添加mockito-core的正确版本。
testImplementation ('org.springframework.boot:spring-boot-starter-test') 
 {
   exclude group: 'org.mockito', module :'mockito-core'
 }

现在包含mockito-inline

testImplementation 'org.mockito:mockito-inline:4.6.1'

这将解决问题。

2
以下文章分享了一个帮助我的替代方案:https://www.baeldung.com/mockito-mock-static-methods#configuring-mockito-for-static-methods。这使我能够配置现有的mockito扩展以允许内联mocks。 - BrandonLenz
你在哪个文件中排除了Mockito核心? - meh93
在 build.gradle 文件中,我们定义依赖项。 - Sanjay Bharwani

12

7

仅适用于Java11,尝试在Java8上运行,无法工作。 - Abdul Mohsin

6

我有一些解决方案步骤。

请使用以下内容替换您的依赖项。

<dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-inline</artifactId>
                <version>4.5.1</version>
                <scope>test</scope>
            </dependency>

如果使用4.5.1或更高版本,你的代码将能够正常工作。

同时,使用try with resource来模拟静态方法。

try (MockedStatic<WelcomeUtil> theMock = Mockito.mockStatic(WelcomeUtil.class)) {
            theMock.when(() -> WelcomeUtil.generateWelcome("John"))
                   .thenReturn("Guten Tag John");

            assertEquals("Guten Tag John", WelcomeUtil.generateWelcome("John"));
        }

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

0
我在尝试使用Mockito5和JDK8时遇到了这个问题,问题是JDK8不支持"Instrumentation API"(这是允许您在运行时模拟静态类的功能),而该功能在JDK9及更高版本中可用。
"Instrumentation API"允许您修改在JVM中加载的现有字节码。
解决方案1: 以下是我为克服此问题所做的解决方法:
dependencies{
....
    testImplementation group: 'org.mockito', name: 'mockito-inline', version: '4.8.0'

    implementation group: 'net.bytebuddy', name: 'byte-buddy-agent', version: '1.12.14'
}

configurations {
    byteBuddyAgent
    compile.extendsFrom byteBuddyAgent
}

test {

    // Add the Byte Buddy agent to the JVM arguments during testing, which will be retrieved by the inline-mockito during runtime.
    doFirst {
        // Get the list of dependencies.
        List<File> dependencies = configurations.compileClasspath.files.toList()

        // Find the jar file for the `byte-buddy-agent` dependency.
        File byteBuddyAgent = dependencies.find { file -> file.path.contains('byte-buddy-agent') }

        if (byteBuddyAgent != null) {
            // Get the path to the jar file.
             jarPath = byteBuddyAgent.absolutePath
             jvmArgs '-javaagent:' + jarPath
        } else {
            // Handle the case when the desired JAR file is not found.
            println "The 'byte-buddy-agent' jar file was not found in the dependencies."
        }
    }

    finalizedBy jacocoTestReport // report is always generated after tests run
}

解决方案2: 对我来说,使用PowerMockito并不实际,因为我需要运行jacoco报告来查看代码覆盖率,而jacoco对PowerMockito并不友好,原因不明。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接