Android Bundle简单的单元测试不起作用。

12

我对安卓开发还比较新,正在努力理解bundle的工作原理。

以下单元测试阻塞了我。有人可以解释一下为什么它失败了吗?

@Test
public void testBundle() throws Exception {
    Bundle bundle = new Bundle();
    String key = "hello";
    String value = "world";
    bundle.putString(key, value);
    Assert.assertEquals(value, bundle.getString(key));
}

junit.framework.ComparisonFailure: 
Expected :world
Actual   :null

你使用的是什么环境?Instrumentation Test、Robolectric、JUnit还是其他的? - Vasiliy
嗨,Vasiliy JUnit测试 - Hui Wang
2个回答

20
JUnit测试在本地机器上运行,该机器没有所有Android源代码,只有存根类(这里描述)。这些存根类允许您针对它们编译Android应用程序(因为它们的API与实际的Android框架相同),但它们不包含任何逻辑以使它们“轻量级”。默认情况下,如果尝试调用任何存根方法,则会收到异常。类似于这样:
public Bundle() {
    throw new RuntimeException("Stub!");
}

这种“快速失败”方法被采用是为了防止开发人员意外地对这些存根类运行他们的代码,然后想知道为什么它不起作用。
然而,可以通过在build.gradle中进行以下配置来更改此行为:
android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }
}

这使得存根方法返回默认值而不是抛出异常。
您可能已启用此功能,因此当运行JUnit测试时,您不会收到异常,但Bundle#getString()方法只返回默认值(即null)。
如果要测试具有Android框架依赖项的代码,则应执行以下操作之一:
  1. 模拟这些依赖关系(例如Mockito)
  2. 使用Robolectric运行测试
  3. 在Android设备上运行仪器测试
无论如何,unitTests.returnDefaultValues = true都是一个非常危险的功能,因为它使您的测试不可靠:某些测试可以通过存根方法返回的默认值而通过,但在实际设备上该功能将失败。关闭它。

确实,我已启用此选项! - Hui Wang
@HuiWang 那你应该肯定要禁用它,然后考虑接受这个答案)。 - Vasiliy
非常感谢您的回答,我真的很感激。另一个问题是,是否有可能在没有模拟器的情况下运行仪器化测试?这使我能够在CI中运行测试。 - Hui Wang
@HuiWang,有一些“设备农场”拥有各种设备,并公开API,允许您将APK上传到其中一个或多个设备上,运行仪器测试,并获取测试结果。这个过程可以自动化并在CI期间执行,但我从未使用过它。也许你应该在这个话题上问另一个问题。 - Vasiliy

4
构建本地单元测试中所述,默认情况下,Gradle的Android插件会对android.jar库进行修改,并执行本地单元测试,这个库并不包含实际代码。因此,你的测试代码运行在已削减的Bundle版本上,该版本并不包含实际实现,因此每当你尝试从其中获取内容时,会得到null。如果你确实想测试一个Bundle的行为,我建议编写一个运行在Android设备上,并针对真正的Bundle实现进行测试的仪表测试。

谢谢Egor,你的回答很有帮助,但Vasiliy的更完整。谢谢 :) - Hui Wang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接