【问题标题】:Android methods are not mocked when using Mockito使用 Mockito 时不会模拟 Android 方法
【发布时间】:2015-10-24 23:07:47
【问题描述】:

我正在编写一个测试,目的是测试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 unit-testing mockito


    【解决方案1】:

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

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

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

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

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

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

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

    【讨论】:

    • 我认为这可能是支持库没有像 androd.jar 那样被替换的情况:“在运行时,将针对 android.jar 的修改版本执行测试,其中所有最终修饰符都已被剥离”。我正在测试在构造函数中采用Adapter 的类,但我需要验证是否在此适配器上调用了通知方法,因此我认为 1. 解决方案无济于事。我已经在使用 Roblectric 但它太慢了,这就是我试图模拟它的原因。我对 PowerMock 很熟悉,但我不想添加额外的测试依赖项——看起来我别无选择。谢谢杰夫!
    • 还有另一种选择:仪器化测试。它们很慢,但您可以在测试中使用 Android 类。见medium.com/@yair.kukielka/…
    • @Yair:尽管插桩测试是用真实实现(在设备或模拟器中)进行测试的有效方法,但它们更不适合在原始问题中使用模拟。跨度>
    • @JeffBowman 我同意,您可以使用 Mockito 或创建仪器测试,但两者都会有点奇怪。我认为 Mockito 有时会让 UI 测试变得有点麻烦。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-13
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2014-08-30
    • 2020-12-08
    • 2014-02-02
    相关资源
    最近更新 更多