尝试使用 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
我使用 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