使用JUnit和JDK测试Android代码

11
我正在为我的Android代码编写一些POJO测试。
我想使用JDK本地运行它们(而不是在模拟器上的Dalvik)——为了速度,使用JUnit 4、Mockito,并且能够在没有设备的情况下运行headless——因此我在Eclipse中有一个单独的“Java”项目。
如果我要测试的方法恰好引用了来自Android SDK的任何内容,例如android.util.Log,则测试将失败——这很有道理,因为android.jar不在类路径中。为了重现这个问题,我有这个测试用例:
public class FooTests {
  @Test
  public void testFoo() {
    android.util.Log.d("x", "y");
  }
}

如果我将android.jar明确添加到测试项目的类路径中,我会遇到以下异常。
java.lang.RuntimeException: Stub!
  at android.util.Log.d(Log.java:7)
  at com.example.FooTests.testFoo(FooTests.java:39)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  ...
  at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

有没有一种方法可以使代码在不模拟每个Android SDK依赖项的情况下工作?也许已经存在一个模拟的android.jar

编辑:目前,我将类(如android.util.Log)包装并将它们注入实例中进行了传统的基于IOC的测试。当我需要时,Scott的PowerMock建议是我的下一步。

稍后编辑Robolectric

5个回答

4

我曾经也遇到过同样的问题。我想本地测试简单的POJOs。
具体来说,我的代码需要使用android.util.Base64。
最终我做的是使用SDK安装Android 4源代码,并将android.util.Base64类复制到我的项目中。
出人意料的是,这种方法奏效了。


是的,最后我创建了一个名为"Base64Encoder"的接口,在应用程序中使用android.util.Base64实现,在测试中使用sun.misc.BASE64Decoder实现 :) 如果能够简单地拥有一个包含所有库代码的JAR文件,并且只需将与运行环境相关的框架部分替换掉,那将会非常棒。 - orip

3
我所知道的没有任何模拟出来的android.jar。我使用Powermock来进行所有内容的模拟。为了帮助处理大量的模拟过程,您可以将扩展的android类,如activity、fragment、broadcast和其他东西设计得更轻巧,并让它们委托给POJO类。您可以根据风险做出决策,不隔离单元测试android扩展类,而是通过android测试框架或其他工具(如Robotium)进行集成单元测试。
对于真正的隔离单元测试,在Android中,对我来说,在java jvm上进行单元测试,并模拟所有协作类是最好的方法。

1
+1 很好的建议,我会尝试使用PowerMock。除了 https://sites.google.com/site/androiddevtesting/ 之外,还有其他相关链接吗? - orip
1
我之前没有看到过那个链接,但它非常好,谢谢分享。我发现自己在尝试模拟Android jar类时经常参考Powermock网站上的“用法”部分,其中列出了各种必要的方法:http://code.google.com/p/powermock/. - Scott

2
我有同样的问题。我想在本地测试简单的业务逻辑。具体来说,我的代码使用了android.util.SparseArray<E>
我尝试了三种不同的方法来使它在android之外可用于junit测试:
1. 在我的业务逻辑中,我创建了一个接口,并将其附加到MySparseArray<E>上,该类继承自SparseArray<E>。在junit测试中,我使用HashMap<Integer, E>重新实现了接口。这个方法可以工作,但是如果需要其他android.*类,那么在长期内这将是太多的工作来使业务逻辑可单元测试。
2. 如@Tal Weiss所建议的,我添加了所需类的android java源代码:android.util.SparseArray.java,它使用了com.android.internal.util.ArrayUtils.java。这也可以工作,但是我不喜欢添加更多的android源代码,特别是如果有更多的依赖关系。
3. 我从http://grepcode.com/snapshot/repository.grepcode.com/java/ext/com.google.android/android/2.2_r1.1下载了一个无存根的二进制android.jar,用作我的junit-java项目中的库。这个jar只包含命名空间android.*com.android.*dalvik.*framework.base.*的类。这也可以工作。
目前,我成功地避免了在我的业务层中使用除SparseArray<E>之外的android.*类,并且没有依赖于Context、Activity或Service。在android业务层中,我本来可以使用HashMap<Integer, E>代替SparseArray<E>

我尝试使用https://github.com/bjoernQ/unmock-plugin在JUnit测试中使用SparseArray,它起作用了,但在unMock部分添加keepStartingWith“dalvik.system.”后。 - Alexey

1

0

5
到目前为止,我已经使用这个工具在模拟器或真实设备上运行InstrumentationTestRunner来进行测试。当我想要测试一个活动或服务时,这是合适的,但当我只想测试与Android平台无关的一些简单逻辑时,就不太合适了。我是否遗漏了什么? - orip
我认为第一项所说的是“普通的junit”测试,针对您的“普通的逻辑” :) - KevinDTimm
当然可以,但据我所知,我要么必须在模拟器或设备上运行它们,要么像现在一样得到“Stub!”异常。 - orip

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