欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Mockito与PowerMock使用问题:Argument should be a mock, but is: class java.lang.Class

程序员文章站 2022-04-27 10:57:35
...

问题描述

  对公司项目的底层依赖包进行升级,在运行成功后打包时出现了问题,测试无法通过,抛出异常:

org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class java.lang.Class

	at com.eastmoney.emis.service.IPOServiceTest.getEmptyContentTest(IPOServiceTest.java:132)
	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

问题分析

  这次底层包升级虽然动了部分代码和工具,但并没有动测试部分代码,初步锁定在依赖包的问题上。

  搜索了一下网上类似的错误,基本上都是说Powermock本身就存在bug

  查看作者对于这个bug的一段描述:

When using Powermocks mockStatic and then calling the when(…).thenReturn(…) method combination of Powermock, the thenReturn method will fail due to a change in Mockito 2.26.1.

See mockito/[email protected]#diff-e4b9e3c44b9c51ed8b23dd318259b1aaR51

As you can see the isVoid check has changed to gather some information from the mock contained in the invocation object.

Unfortunately this mock is not the mocked object but instead the original class which was used to call the static method.

Due to that Mockito complains about this via org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class java.lang.Class

  也就是说2.26.1版本后,isVoid已经更改为从调用对象包含的mock中获取信息。但是,这个mock不是模拟的对象,而是用来调用静态方法的原始类。所以,这个使用模拟对象传入会抛出上述错误。

  大致解决方案是将MockitoPowermock版本对应。

  项目中依赖包如下:

<!-- PowerMock JUnit 4.4+ Module -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>3.5.10</version>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.7</version>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.2</version>
</dependency>

  明显Mockito版本过高,但是项目原先使用并无问题,所以问题一定是出现在版本升级的依赖包里。

  升级Springboot的主包:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7.RELEASE</version>
</parent>

  将2.1.7.RELEASE升为2.3.3.RELEASE

  升级公司的基础包:

<dependency>
    <groupId>com.eastmoney.emis</groupId>
    <artifactId>emis-spring-boot-starter</artifactId>
    <version>2.1.6.RELEASE</version>
</dependency>

  将2.1.6.RELEASE升为2.2.1.RELEASE

  由上面的描述可以知道出现问题的是org.mockito.internal.stubbing.answers.InvocationInfo.isVoid,在mockito-core包下。而包含这个子包的有spring-boot-starter-parentmockito-inlinepowermock-api-mockito2

  根据Maven的加载顺序可知,此时我们项目加载的是Springboot中自带的包,可以直接从项目的Maven库中找到,在升级之前,引用的为mockito-core:2.23.4。在升级后,引用的为mockito.core:3.3.3,版本过高,与PowerMock版本不匹配。

问题解决

  解决办法很简单,直接声明mockito-core的版本就行:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.23.4</version>
</dependency>

问题补充

  为简化依赖包,我们可以直接将mockito-inlinemockito-core替代即可,但是又会出现问题:

java.lang.RuntimeException: Invoking the beforeTestMethod method on PowerMock test listener [email protected] failed.
...
Caused by: org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class sun.security.rsa.RSAPublicKeyImpl
Mockito cannot mock/spy because :
 - final class
...

  此时又会出现因为mockito版本太低不支持final类的模拟,需要进行版本升级。

  解决方案有几种:

  1. 不更换依赖包,对代码进行一定的调整(不推荐)
  2. 调整依赖包版本,在保证版本足够高时,同时也与powermock版本相匹配,并调整测试代码(推荐:虽然比较复杂耗时,但一劳永逸)
相关标签: mockito