【问题标题】:Cannot run powermock due to "Unexpected exception" and "org.mockito.exceptions.misusing" errors由于“意外异常”和“org.mockito.exceptions.misusing”错误,无法运行 powermock
【发布时间】:2021-09-30 15:08:53
【问题描述】:

我有 Java 版本 11,JUnit 版本 4.13.1 并尝试为我的单元测试申请 powermock。我添加了this页面上提到的必要库:

JUnit 4.4 或更高版本:

<properties>
    <powermock.version>2.0.2</powermock.version>
</properties>
<dependencies>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-module-junit4</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.powermock</groupId>
      <artifactId>powermock-api-mockito2</artifactId>
      <version>${powermock.version}</version>
      <scope>test</scope>
   </dependency>
</dependencies>

然后更新我的单元测试,如下所示:

DemoUnitTest:

@RunWith(PowerMockRunner.class)
@PrepareForTest(Locale.class)
public class DemoUnitTest {

    @InjectMocks
    private DemoServiceImpl demoService;

    @Test(expected = EntityNotFoundException.class)
    public void test_PowerMock() {
        String countryCodeUpper = countryCode.toUpperCase(Locale.ENGLISH);

        PowerMockito.mockStatic(Locale.class);
        BDDMockito.given(Locale.getISOCountries()).willReturn(new String[]{});

        demoService.create(countryCode);
    }
}

那么,错误在哪里?

这是堆栈跟踪:

java.lang.Exception: Unexpected exception, expected<com.mycompany.common.domain.exception.EntityNotFoundException> but was<org.mockito.exceptions.misusing.NotAMockException>

    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.handleException(PowerMockJUnit44RunnerDelegateImpl.java:380)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.handleException(PowerMockJUnit47RunnerDelegateImpl.java:126)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.handleInvocationTargetException(PowerMockJUnit44RunnerDelegateImpl.java:353)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:331)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:310)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:298)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:218)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:160)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:134)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
    at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:136)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:117)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:57)
    at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class java.lang.Class
    at com.mycompany.core.service.impl.unit.CountryServiceImplTest.test_PowerMock(CountryServiceImplTest.java:191)
    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)
    ... 5 more

【问题讨论】:

  • 你能用堆栈跟踪添加异常吗?
  • @Alex 虽然我没有尝试过使用 Java 11 ,但你可以尝试使用 PowerMockito.when(Locale.getISOCountries()).thenReturn(new String[]{});
  • 我也尝试过,如文档中所述,但仍然是同样的问题。我正在尝试通过类似when(SSLContext.getInstance(anyString())).thenAnswer((Answer&lt;SSLContext&gt;) invocation -&gt; sslContextMock); 的方式使用,但不确定它是否有效
  • @talex 我添加了堆栈跟踪,有什么帮助吗?
  • @talex Chico,你在吗?

标签: java unit-testing testing mockito powermock


【解决方案1】:

将您的 @PrepareForTest(Locale.class) 更改为 @PrepareForTest(DemoServiceImpl.class)

Locale,来自java.util 包,是一个系统类。 PowerMockRunner 无法拦截它的加载,因为 Java 已经加载了它。因此,您必须在 use the special rule for mocking system classes 中反转正常规则:

通常您会准备包含您喜欢模拟的静态方法(我们称之为 X)的类,但因为 PowerMock 不可能准备系统类进行测试,所以必须采用另一种方法。因此,您无需准备 X,而是准备调用 X 中的静态方法的类!

有些有意义:@PrepareForTest 指示 PowerMock 应该拦截哪些类并重写字节码。因为您无法拦截和字节码重写 Locale,下一个最好的方法是拦截和字节码重写您的测试系统,以便它调用 PowerMock 重写的 Locale 而不是 Java 的 Locale。


通常,模拟您的被测系统被视为一种不好的做法,但这有点例外:只要您实际上没有模拟自己的被测系统,这更像是创建一个测试在你的课堂上接缝。不存在意外测试您的模拟行为而不是测试您的预期系统的危险。也就是说,您可以考虑进行重构:

  • 您可以将 Locale.getISOCountries() 保存到您在测试中替换的 "visible for testing" 字段中,或者将其作为可选的构造函数参数,在不存在时遵循 Locale.getISOCountries()
  • 您可以将对 Locale.getISOCountries() 的调用提取到一个 lambda(例如 Supplier&lt;String[]&gt;),并同样将其设为可见字段或可选的构造函数参数。
  • 您可以将调用提取到 DemoServiceImpl 上的可覆盖方法中并在测试中覆盖它,但这也存在实现和测试代码之间边界模糊的风险。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-08
    • 2014-01-13
    • 1970-01-01
    • 2014-09-24
    • 1970-01-01
    • 1970-01-01
    • 2015-06-02
    相关资源
    最近更新 更多