PowerMockito最终方法调用验证无效

时间:2016-03-08 16:37:58

标签: java junit mockito powermock

我正在测试一个运行的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)的情况下更改验证呼叫。没帮忙
  • 将powermockito版本升级到1.6.3。没帮忙
  • MyTask 添加到 MyTaskTest 作为内部类,它以某种方式工作,这意味着验证调用已通过,但我希望在 MyTask 中其他文件作为单独的类,仍然无效。

2 个答案:

答案 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"

附注:

  • 特别是对于您的用例,您可能根本不需要模拟AtomicBoolean;在不同的线程上使用真实的,测试线程是否存活,设置布尔值,并测试线程是否已死。您可能需要poll over time,就像Mockito为其timeout验证模式所做的那样。
  • 不要忘记stopReceiving.get() == true始终可以替换为stopReceiving.get()

答案 1 :(得分:0)

MyTask.class添加到@PrepareForTest注释修复了问题。

...
@RunWith(PowerMockRunner.class)
@PrepareForTest({AtomicBoolean.class, MyTask.class})
public class MyTaskTest {
...

我无法找到有关为何需要的任何信息。唯一的解释是 PowerMockito 以某种方式将最终方法视为特殊方法,因此为了使验证有效,您需要添加一个使用模拟的类和最终方法调用嘲笑。