java - PowerMockito final方法调用验证不起作用

标签 java junit mockito powermock

我正在测试一个运行直到AtomicBoolean值发生变化的任务类。我使用 PowerMockito 是因为 AtomicBoolean 实例的 get() 方法是 final方法。

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(); 失败并显示:

Wanted but not invoked java.util.concurrent.atomic.AtomicBoolean.get(); Actually, there were zero interactions with this mock.

这对我来说没有意义,因为它显然是在 run() 方法中调用的。我什至调试了它,它通过了 run 方法,但不知何故没有增加它的调用计数器。我在这里做错了什么?

附:我使用的是 PowerMockito 版本 1.6.2

我尝试过的

  • 将验证调用更改为不使用 times(1) 进行调用。没有帮助
  • 将 powermockito 版本升级到 1.6.3。没有帮助
  • MyTask 添加到 MyTaskTest 作为内部类,这在某种程度上起作用了,这意味着验证调用已通过,但我希望将 MyTask 包含在其他文件作为单独的类,仍然不起作用。

最佳答案

您遇到这个麻烦是因为您试图模拟系统类。如specified in the PowerMock documentation ,PowerMock无法准备系统类进行测试,因此您需要准备调用系统类的类,就像您在答案中所做的那样。

@RunWith(PowerMockRunner.class)
@PrepareForTest(MyTask.class)
public class MyTaskTest {
<小时/>

底层:普通 Mockito 的工作原理是动态创建您正在模拟的类的子类,然后返回该子类;向 Mockito 的委托(delegate)是通过正常的多态性发生的。使用 final 方法,Java 不需要为动态分派(dispatch)执行虚拟方法查找; Java 在编译时知道要调用哪个实现,并在 Mockito 介入之前通过静态调度调用该实现。

PowerMock 通过重写(字节码操作)包含 final方法的类来解决此问题,以便实现更改为委托(delegate)给 EasyMock 或 Mockito stub 和模拟;不过,为了使其生效,PowerMock 必须尽早在类路径中安装修改后的类(此处为 AtomicBoolean),以覆盖它可以找到的任何其他实现。

但是,您永远无法安装优先级高于系统类的替换,因为它们已经加载到根类加载器中;没有什么可以先发制人。 PowerMock 通过重写您的被测系统来解决这个问题,将对 AtomicBoolean.get() 的调用替换为对 PowerMock 可以修改的替换方法的调用。因此,并不是“PowerMockito 以某种方式将 final方法视为特殊的”,而是 javac 将它们视为特殊的(使用静态调度调用),而 PowerMock 将系统类视为特殊的特殊(因为它不能以通常的方式覆盖它们)。

<小时/>

相关阅读: Lukáš Krejčí 的 "The Dark Powers of PowerMock"

旁注:

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

关于java - PowerMockito final方法调用验证不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35872914/

相关文章:

java - 如何在 Java 中从 String 创建 XML 对象?

java - 为什么在使用 @RunWith(SpringJUnit4ClassRunner.class) 时,JUnit 4.12 会针对 assertEquals 抛出 NoSuchMethodError?

java - JUnit 测试失败时显示用户友好的消息

java - Micronaut:测试失败,找不到页面,但可以通过浏览器正常访问该页面

java - Mockito 模拟构造函数示例

java - 如何编写单元测试用例来验证签名?

java - 用于 REST + DAO 对象的 Mockito

java - JVM 字节码浮点否定 : not allowed but possible?

java - Eclipse 内容协助不显示方法描述

java - 为什么空的 else-if 语句风格不好,我应该如何重写它?