尝试使用 Powermockito 验证静态方法时出现 NotAMockException 异常

NotAMockException exception when trying to verify a static method with Powermockito

我使用 PowerMock 来测试其文档中提到的静态方法。

据我所知,可能存在错误,但我不确定:

Static mocking broken for Mockito >= 2.26.1

...

我尝试了以下页面中提到的变通办法,但它并没有解决问题,而且其中一些变通办法已经过时,因此无法使用。

verifyStatic get NotAMockExcption from mockito

但是,我得到 “传递给 verify() 的参数是 Class 类型并且不是模拟!” 错误。这是我正在测试的服务方法和测试方法:

服务:

// I want to test this method
 public CommandDTO create(EmployeeRequest request) {
    // ...

    log();
    return CommandDTO.builder().uuid(employee.getUuid()).build();
}


private void log() {
    LoggingUtils.info("Created...");
}

测试:

@RunWith(PowerMockRunner.class)
@PrepareForTest(LoggingUtils.class)
public class EMployeeServiceImplTest {

    @Test
    public void unit_test() {

        // ...

        PowerMockito.mockStatic(LoggingUtils.class);

        employeeService.create(request);

        PowerMockito.verifyStatic(LoggingUtils.class); // throws error

        LoggingUtils.info(any());
    }
}

以下是库和版本:

pom.xml:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.4.6</version>
    <scope>test</scope>
</dependency>

由于你在另一条评论中提出了问题,这里是我过去使用的一种模式来防止需要静态模拟:

假设我们有一个静态方法...

public class SomeClass {
  public static String doSomething(int x) { ... }
}

现在想象一下我们的代码像这样使用它...

public class MyClass {
  public void someMethod() {
    String xyz = SomeClass.doSomething(123);
    ...
  }
}

如果我们现在需要模拟那个 SomeStaticClass.doSomething(123) 调用,我们需要静态模拟。

另一种选择是委托人:

public class MyDelegator {
  public String doSomething(int x) { 
    return SomeClass.doSomething(x);
  } 
}

现在您可以在 class:

中使用它
public class MyClass {
  private final MyDelegator myDelegator;

  public MyClass(MyDelegator myDelegator) { this.myDelegator = myDelegator; }

  public void someMethod() {
    String xyz = myDelegator.doSomething(123);
    ...
  }
}

瞧,您可以简单地模拟 MyDelegator 对象,而不必关心原始实现是静态调用。

当然,这并不能解决底层代码异味,但它可以帮助您在无法重构静态代码本身的情况下解决这个问题。

让我提出一个完全不同的方法,因为我看到您已经在使用 Mockito 版本 3.4.6。 由于 Mockito >= 3.4.0 你可以直接模拟静态方法。这意味着您可以完全摆脱 PowerMock 依赖关系。
此外,AFAIK PowerMock 不支持 JUnit5。此解决方案将允许您将 JUnit 测试框架升级到版本 5 而不会出现问题,我也强烈推荐这一点。

使用 Mockito 的静态模拟确实需要您使用 mockito-core 上的 mockito-inline 依赖项,但是,因为 mockito-inline 依赖于 mockito-core,Maven 也会下载 mockito-core包。请注意,Mockito 文档指出,在未来的某一时刻,他们可能会在提取所有这些内容时取消此包 核心包的“实验性”功能。
除此之外,您还应该将 @RunWith(PowerMockRunner.class) 替换为 @RunWith(MockitoJUnitRunner.class)

这就是您的使用方式。

您可以像这样使用自动调用 close

的 try-with-resources 语句定义每个测试方法的模拟
    try (MockedStatic<Files> filesMock = Mockito.mockStatic(Files.class)) {
        filesMock.when(() -> Files.copy(any(Path.class), any(OutputStream.class))).thenReturn(1L);
     
        // Call logic
    }

或者您可以在 @Before 方法中为所有测试定义它,因为您使用的是 jUnit4。

    private MockedStatic<Files> filesMock;

    @Before
    public void init() {
        filesMock = org.mockito.Mockito.mockStatic(Files.class);
    }

    @After
    public void teardown() {
        filesMock.close();
    }
    
    @Test
    void test() {
        filesMock.when(() -> Files.copy(any(Path.class), any(OutputStream.class))).thenReturn(1L);
    }

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mockito-inline https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks