PowerMockito 最终方法调用验证不起作用
PowerMockito final method invoke verification not working
我正在测试一个任务 class,该任务一直运行到 AtomicBoolean 值发生变化。我正在使用 PowerMockito,因为 AtomicBoolean 实例的 get() 方法是最终方法。
MyTaskTest 看起来像这样:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(AtomicBoolean.class)
public class MyTaskTest {
private AtomicBoolean stopReceiving;
private MyTask task;
@Before
public void setUp() {
stopReceiving = PowerMockito.mock(AtomicBoolean.class);
task = new MyTask(stopReceiving);
}
@Test
public void test_run_when_StopReceivingIsFalseFromTheBeginning_should_invokeGetOnlyOnce() {
// given
PowerMockito.when(stopReceiving.get()).thenReturn(false);
// when
task.run();
// then
verify(stopReceiving, times(1)).get();
}
}
和 MyTask class 看起来像这样:
import java.util.concurrent.atomic.AtomicBoolean;
public class MyTask implements Runnable {
private final AtomicBoolean stopReceiving;
MyTask(AtomicBoolean stopReceiving) {
if (stopReceiving == null) {
throw new NullPointerException("stopReceiving is null. This argument should not be null");
}
this.stopReceiving = stopReceiving;
}
@Override
public void run() {
while (stopReceiving.get() == true) {
System.out.println("I should not get to here!");
}
}
}
这里的问题是检查 verify(stopReceiving, times(1)).get();
失败:
Wanted but not invoked
java.util.concurrent.atomic.AtomicBoolean.get(); Actually, there were
zero interactions with this mock.
这对我来说没有意义,因为在 run()
方法中明确调用了它。我什至调试了它,它通过了 run
方法,但不知何故没有增加它的调用计数器。我在这里做错了什么?
P.S。我正在使用 PowerMockito 版本 1.6.2
我试过的
- 更改要在没有
times(1)
的情况下调用的验证调用。没有帮助
- 正在将 powermockito 版本升级到 1.6.3。没有帮助
- 将 MyTask 作为内部 class 添加到 MyTaskTest 中,不知何故有效,这意味着验证调用已通过,但我想要在另一个文件中将 MyTask 作为单独的 class,这仍然不起作用。
将 MyTask.class
添加到 @PrepareForTest
注释解决了这个问题。
...
@RunWith(PowerMockRunner.class)
@PrepareForTest({AtomicBoolean.class, MyTask.class})
public class MyTaskTest {
...
我找不到任何关于为什么需要的信息。唯一的解释是 PowerMockito 以某种方式将 final 方法视为特殊方法,因此为了验证工作,您需要添加一个 class 正在使用带有最终方法调用模拟的模拟。
你 运行 陷入了这个麻烦,因为你试图模拟一个系统 class。由于 specified in the PowerMock documentation,PowerMock 无法为测试准备系统 classes,因此您需要准备调用系统 class 的 class,而不是像您的答案中那样。
@RunWith(PowerMockRunner.class)
@PrepareForTest(MyTask.class)
public class MyTaskTest {
引擎盖下: Normal Mockito 的工作原理是动态创建您正在模拟的 class 的子 class,然后返回它;对 Mockito 的委托是通过正常的多态性发生的。使用 final
方法,Java 不需要为动态调度执行虚拟方法查找; Java 知道在编译时调用哪个实现,并在 Mockito 可以干预之前通过静态调度调用实现。
PowerMock 通过重写(字节码操作)包含最终方法的 class 来解决这个问题,以便将实现更改为委托给 EasyMock 或 Mockito 存根和模拟;但是,为了使其生效,PowerMock 必须在 class 路径中足够早地安装修改后的 class(此处为 AtomicBoolean
),以覆盖它可以找到的任何其他实现。
但是,您永远不能安装优先级高于系统 classes 的替代品,因为它们已经加载到根 classloader 中;没有什么会先发制人。 PowerMock 通过重写您的被测系统来解决 this,将对 AtomicBoolean.get() 的调用替换为对 PowerMock 可以修改。因此,"PowerMockito is somehow treating final methods as special" 并非如此,而是 javac
将它们视为特殊的(通过静态调度调用)并且 PowerMock 将系统 classes 视为特殊的(因为它不能以通常的方式覆盖它们。
相关阅读: Lukáš Krejčí 的 "The Dark Powers of PowerMock"
旁注:
- 特别是对于您的用例,您可能根本不需要模拟 AtomicBoolean;在不同的线程上使用真实的线程,测试线程是否存活,设置布尔值,并测试线程是否已死亡。您可能需要 poll over time, the way Mockito does for its
timeout
验证模式。
- 不要忘记
stopReceiving.get() == true
总是可以替换为 stopReceiving.get()
。
我正在测试一个任务 class,该任务一直运行到 AtomicBoolean 值发生变化。我正在使用 PowerMockito,因为 AtomicBoolean 实例的 get() 方法是最终方法。
MyTaskTest 看起来像这样:
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(AtomicBoolean.class)
public class MyTaskTest {
private AtomicBoolean stopReceiving;
private MyTask task;
@Before
public void setUp() {
stopReceiving = PowerMockito.mock(AtomicBoolean.class);
task = new MyTask(stopReceiving);
}
@Test
public void test_run_when_StopReceivingIsFalseFromTheBeginning_should_invokeGetOnlyOnce() {
// given
PowerMockito.when(stopReceiving.get()).thenReturn(false);
// when
task.run();
// then
verify(stopReceiving, times(1)).get();
}
}
和 MyTask class 看起来像这样:
import java.util.concurrent.atomic.AtomicBoolean;
public class MyTask implements Runnable {
private final AtomicBoolean stopReceiving;
MyTask(AtomicBoolean stopReceiving) {
if (stopReceiving == null) {
throw new NullPointerException("stopReceiving is null. This argument should not be null");
}
this.stopReceiving = stopReceiving;
}
@Override
public void run() {
while (stopReceiving.get() == true) {
System.out.println("I should not get to here!");
}
}
}
这里的问题是检查 verify(stopReceiving, times(1)).get();
失败:
Wanted but not invoked java.util.concurrent.atomic.AtomicBoolean.get(); Actually, there were zero interactions with this mock.
这对我来说没有意义,因为在 run()
方法中明确调用了它。我什至调试了它,它通过了 run
方法,但不知何故没有增加它的调用计数器。我在这里做错了什么?
P.S。我正在使用 PowerMockito 版本 1.6.2
我试过的
- 更改要在没有
times(1)
的情况下调用的验证调用。没有帮助 - 正在将 powermockito 版本升级到 1.6.3。没有帮助
- 将 MyTask 作为内部 class 添加到 MyTaskTest 中,不知何故有效,这意味着验证调用已通过,但我想要在另一个文件中将 MyTask 作为单独的 class,这仍然不起作用。
将 MyTask.class
添加到 @PrepareForTest
注释解决了这个问题。
...
@RunWith(PowerMockRunner.class)
@PrepareForTest({AtomicBoolean.class, MyTask.class})
public class MyTaskTest {
...
我找不到任何关于为什么需要的信息。唯一的解释是 PowerMockito 以某种方式将 final 方法视为特殊方法,因此为了验证工作,您需要添加一个 class 正在使用带有最终方法调用模拟的模拟。
你 运行 陷入了这个麻烦,因为你试图模拟一个系统 class。由于 specified in the PowerMock documentation,PowerMock 无法为测试准备系统 classes,因此您需要准备调用系统 class 的 class,而不是像您的答案中那样。
@RunWith(PowerMockRunner.class)
@PrepareForTest(MyTask.class)
public class MyTaskTest {
引擎盖下: Normal Mockito 的工作原理是动态创建您正在模拟的 class 的子 class,然后返回它;对 Mockito 的委托是通过正常的多态性发生的。使用 final
方法,Java 不需要为动态调度执行虚拟方法查找; Java 知道在编译时调用哪个实现,并在 Mockito 可以干预之前通过静态调度调用实现。
PowerMock 通过重写(字节码操作)包含最终方法的 class 来解决这个问题,以便将实现更改为委托给 EasyMock 或 Mockito 存根和模拟;但是,为了使其生效,PowerMock 必须在 class 路径中足够早地安装修改后的 class(此处为 AtomicBoolean
),以覆盖它可以找到的任何其他实现。
但是,您永远不能安装优先级高于系统 classes 的替代品,因为它们已经加载到根 classloader 中;没有什么会先发制人。 PowerMock 通过重写您的被测系统来解决 this,将对 AtomicBoolean.get() 的调用替换为对 PowerMock 可以修改。因此,"PowerMockito is somehow treating final methods as special" 并非如此,而是 javac
将它们视为特殊的(通过静态调度调用)并且 PowerMock 将系统 classes 视为特殊的(因为它不能以通常的方式覆盖它们。
相关阅读: Lukáš Krejčí 的 "The Dark Powers of PowerMock"
旁注:
- 特别是对于您的用例,您可能根本不需要模拟 AtomicBoolean;在不同的线程上使用真实的线程,测试线程是否存活,设置布尔值,并测试线程是否已死亡。您可能需要 poll over time, the way Mockito does for its
timeout
验证模式。 - 不要忘记
stopReceiving.get() == true
总是可以替换为stopReceiving.get()
。