使用 Mockito 时不会模拟 Android 方法

标签 android unit-testing mockito

我正在编写一个测试,目的是测试 Adapter 类(由 RecyclerView 使用)的子类中的一些方法。为了模拟适配器行为,我选择了 Mockito 库。

这是一个简单的测试:

import android.support.v7.widget.RecyclerView.Adapter;

public class TestAdapter {

    @Test
    public void testAdapter() throws Exception {
        Adapter adapter = Mockito.mock(Adapter.class);

        adapter.notifyDataSetChanged();
    }
}

什么都不应该发生,而是抛出 NullPointerException

java.lang.NullPointerException
    at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:5380)
    at pl.toro.test.adapter.TestAdapter.testAdapter(TestAdapter.java:38)
    ...

我也尝试过使用 spy 而不是 mock

@Test
public void testAdapterWithDoNothing() throws Exception {
    Adapter adapter = Mockito.spy(new Adapter() {...});
    doNothing().when(adapter).notifyDataSetChanged();
}

但这在第二行失败了(注意这只是一个模拟设置)

java.lang.NullPointerException
    at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:8946)
    at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:5380)
    at pl.toro.test.adapter.TestAdapter.testAdapterWithDoNothing(TestAdapter.java:59)
    ...

是否可以在单元测试中使用 Mockito 模拟支持类?

最佳答案

Android 支持类服从 the same rules as the rest of Java关于什么可以或不能被 mock ;不幸的是,您所指的方法(notifyDataSetChanged)是最终的。您可能还认为这是模拟您不拥有的类型的危险,因为final 语义会影响该类是否可以被模拟。

在内部,Mockito 通过创建您正在模拟的类的“子类”(实际上是代理实现)来工作。因为 final 方法解析可以在编译时发生,Java 跳过多态方法查找并直接调用您的方法。这在生产中稍快一些,但如果没有方法查找,Mockito 将失去其重定向调用的唯一方法。

这在 Android SDK 和支持库中很常见(最近用 a modified version of android.jar 解决了 final 修饰符)和支持库。您可能会考虑以下解决方案之一:

  1. 重构您的系统以引入您控制的包装器类,它非常简单,几乎不需要测试。

  2. 使用 Robolectric ,它使用一个特殊的以 Android-SDK 为目标的 JVM 类加载器,可以将调用重写为可模拟的等效项(并且还可以解析和提供 Android 资源,以便在没有模拟器的情况下进行测试)。 Robolectric 提供了足够多的特定于 Android 的“阴影”(替换实现),您通常可以避免编写自己的阴影,但也允许为支持库等情况自定义阴影。

  3. 使用 PowerMock ,它使用特殊的类加载器扩展了 Mockito 或 EasyMock,该类加载器还重写了被测系统的字节码。与 Robolectric 不同,它不是针对 Android 的,这使其成为一种更通用的解决方案,但通常需要更多设置。

    PowerMock 是 Mockito 的扩展,因此它提供了更多的功能,但我个人尝试重构任何需要它的问题。

关于使用 Mockito 时不会模拟 Android 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31777188/

相关文章:

Android _ 从服务类重定向到另一个 Activity ?

android - 如何获取可执行文件的属性 C

java - 使用 java 8 编写的 DAO 类方法的 Mockito 测试用例

java - 使用 Mockito 模拟结果集

android - 未完成的 stub 异常

Android 为图像文件创建自定义 Exif 属性

Android 相机 - 将图像保存到 SD 卡中的新文件夹中

python - 使用nosetests --processes 生成 xml 输出

java - 为什么 Mockito.verify 未能通过与我放置的测试不同的测试?

ruby-on-rails - 使用 Ruby 临时设置系统时间以进行单元测试