我正在测试一个运行的Task类,直到 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 类看起来像这样:
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();
失败了:
通缉但没有被援引 java.util.concurrent.atomic.AtomicBoolean.get();实际上,有 与此模拟零互动。
这对我没有意义,因为它在run()
方法中被明确调用。我甚至调试它并通过run
方法,但不知何故没有增加调用计数器。我在这做错了什么?
附:我正在使用PowerMockito版本1.6.2
我尝试了什么
times(1)
的情况下更改验证呼叫。没帮忙答案 0 :(得分:2)
你遇到了这个麻烦,因为你正试图模拟一个系统类。作为specified in the PowerMock documentation,PowerMock无法为测试准备系统类,因此您需要准备调用系统类的类,而不是在答案中。
@RunWith(PowerMockRunner.class)
@PrepareForTest(MyTask.class)
public class MyTaskTest {
引擎盖下:正常的Mockito通过动态创建你正在嘲笑的类的子类来工作,然后返回它; Mockito代表团通过正常的多态性发生。使用final
方法,Java不需要为动态分派执行虚拟方法查找; Java知道在编译时调用哪个实现,并在Mockito可以干预之前通过静态调度调用实现。
PowerMock通过重写(字节码操作)包含最终方法的类来解决这个问题,以便实现更改为委托给EasyMock或Mockito存根和模拟;但是,为了使其生效,PowerMock必须在类路径中尽早安装修改后的类(AtomicBoolean
),它会覆盖它可以找到的任何其他实现。
但是,您不能安装优先级高于系统类的替换项,因为它们已经加载到根类加载器中;什么都不会先发制人。 PowerMock通过重写正在测试的系统来解决 this ,通过调用PowerMock 可以修改的替换方法替换对AtomicBoolean.get()的调用。因此,并非“PowerMockito以某种方式将最终方法视为特殊方法”的情况,javac
将它们视为特殊(使用静态调度调用)并且PowerMock将系统类视为特殊(因为它不能以通常的方式覆盖它们。)
相关阅读:LukášKrejčí的"The Dark Powers of PowerMock"
附注:
timeout
验证模式所做的那样。stopReceiving.get() == true
始终可以替换为stopReceiving.get()
。答案 1 :(得分:0)
将MyTask.class
添加到@PrepareForTest
注释修复了问题。
...
@RunWith(PowerMockRunner.class)
@PrepareForTest({AtomicBoolean.class, MyTask.class})
public class MyTaskTest {
...
我无法找到有关为何需要的任何信息。唯一的解释是 PowerMockito 以某种方式将最终方法视为特殊方法,因此为了使验证有效,您需要添加一个使用模拟的类和最终方法调用嘲笑。