【问题标题】:unit test android单元测试安卓
【发布时间】:2012-04-19 05:35:07
【问题描述】:

对 android 应用程序进行单元测试通常比预期的要困难,因为有些类没有公共构造函数(如 NotificationManager),有些类具有无法覆盖的方法,如 Context.getText。

对于 NotificationManager,我使用了委托人。我会对上下文做同样的事情,但这意味着在所有使用上下文(很多)的类中,我需要使用我自己的上下文,甚至不能从上下文派生。然后,每当我需要将上下文传递给 Android API 时,我都需要从我的包装器中获取真正的上下文。

这是正确的吗?还有另一种方法吗?将这些方法声明为 final 的理由是什么? 是否有人真正为 Android 的大型应用程序编写过单元测试?

编辑

我找到了为什么 getText 方法被定义为 final 的原因:它是一个模板方法。所以覆盖调用的非final方法就足够了

我还发现 this 有效,但有点奇怪

【问题讨论】:

  • 我也使用自定义的 android.app.Application 并认为进入测试项目时非常方便,您可以在这里定义许多应用程序范围的变量并通过 getter/setter 在测试中获取/设置这些变量Project,获取高度可配置的应用程序对象,很像模拟上下文。有什么奇怪的?也许您可以打开另一个问题进行进一步讨论。

标签: android unit-testing


【解决方案1】:

问题有点模糊,所以我要写一个长答案。

有些类没有公共构造函数(如 NotificationManager)

因为有一些系统范围的资源/组件(例如 NotificationManager),Android 不喜欢应用程序按需构建/实例化,相反,这些资源由系统集中和管理,应用程序应始终使用系统提供的 API获取它们。

以及具有无法覆盖的方法的类,例如 Context.getText

通常在 Java 中,标记为 final 的方法意味着它们受 API 保护,并且 API 开发人员不希望应用程序开发人员覆盖默认行为。特别是 Context.getText() 这个方法,它实际上使用了模板方法模式,查看下面 herschel 的评论和source code 了解更多详情。

那么问题是我们如何正确测试使用这些基于android上下文的资源/组件编写的代码?

Android SDK 给出的答案是测试项目(通过 InstrumentationTestRunner)。一般在使用 InstrumentationTestRunner 的时候,sense 背后,系统会同时安装 test.apk(从测试项目构建)和 app.apk(从应用项目构建),并使用 test.apk 操作 app.apk 进行测试。 无论您使用AndroidTestCase API(下图中称为JUnit)还是​​InstrumentationTestCase API(下图中称为Instrumentation)都会发生这种情况 对应的可测试的基于android上下文的资源/组件(即Activity,NotificationManager或 TextView) 都是从实际运行的 app.apk 实例中获取的。如果您在测试项目中创建/使用自己的 MockContext,您可以从图中看到,模拟对象被 InstrumentationTestRunner 注入到正在运行的应用程序中。

使用仪器测试的缺点在于效率,它进入了完整的运行生命周期(启动模拟器 -> 安装并启动 test.apk 和 app.apk 并启动 InstrumentationTestRunner),即使您只需要测试一个方法来自单个班级(正如您在评论中提到的那样)。我同意 Ollie 的观点,这是一个充满敌意的设计。

这就是Robolectric 进来玩的地方,引用他们的网站:Robolectric 是一个单元测试框架,它可以去除 Android SDK jar 的齿,因此您可以测试驱动您的 Android 应用程序的开发。测试在您的工作站上的 JVM 内运行几秒钟。

根据我自己的经验,我有一个应用程序包含大约 10 个活动和几个服务以及一些其他自定义视图/小部件,主要与远程服务器进行 http 通信,并使用仪器测试(通过测试项目)和 Robolectic 测试 Android 上下文基于代码。只要时间允许,我几乎可以测试我的应用程序的几乎每一个类/公共方法。

【讨论】:

  • 您的意思是要测试我需要运行应用程序的方法吗?我想单独测试该方法,所以没有真正调用 Android sdk。顺便说一句,在 android sdk 中有一个 MockContext 应该用于在测试期间模拟上下文。问题是我没有设法使用它,因为 final 方法。
  • 在您的回答中您说:它进入了完整的运行生命周期(启动模拟器-> 安装并打开 test.apk 和 app.apk 并运行仪器测试)。 “开放”是什么意思?我所看到的是,如果我不使用真实上下文,我可以在不运行应用程序的情况下运行单元测试。我只需要安装它。只有当我调用 Context.getText 时,才会创建扩展“Application”的类(调用 onCreate)。我现在正在安装 Robolectric。
  • 如果您使用 Android 测试项目编写测试代码并通过 InstrumentationTestRunner 运行 Android 测试项目,答案是肯定的。如果您想单独测试该方法而不真正调用 Android sdk,那么强烈建议您考虑使用 Robolectric。 AFAIK,Robolectric 是目前唯一可用的解决方案,它让您无需在模拟器或真实设备上运行实际应用程序(通过 InstrumentationTestRunner)来测试基于 Android 上下文的资源/组件。
  • 我正在使用 AntroidTestCase 并且该应用程序仍然是 tarted。我找到了 getText 方法是 final 的原因:它是一个模板方法。要存根它,覆盖被调用的方法就足够了
  • 我会将其设置为已接受,但您需要更正不正确的内容:getText 是最终结果的原因,AndroidTestCase 应用程序未启动的事实。老实说,我认为说您很少需要模拟上下文是不正确的。但这只是你的意见......
【解决方案2】:

Android 在许多方面都非常反对测试。

您可能想看看 Roboelectric http://pivotal.github.com/robolectric/

我假设你已经阅读了这些:

http://developer.android.com/guide/topics/testing/index.html

http://developer.android.com/resources/tutorials/testing/helloandroid_test.html

【讨论】:

    【解决方案3】:

    Android 为您提供的 *TestCase 类应该能够解决您遇到的测试问题。在大多数情况下,它会为您生成一个上下文,您可以根据需要覆盖该上下文。我建议从那里开始,或者询问有关如何测试一件事或另一件事的更具体的问题。

    【讨论】:

    • 问题是,如果你没有在你的代码中包装 Context 类,我看不到只处理测试用例来解决问题的方法。最终的方法不能被存根。例如,在我的例子中,当我只是测试一个类的方法时,会启动应用程序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    • 1970-01-01
    • 2010-10-22
    • 2014-08-09
    • 1970-01-01
    相关资源
    最近更新 更多