为什么 Android 测试如此困难:历史版本

简介: 本文讲的是为什么 Android 测试如此困难:历史版本,大约两年以前,我写了两篇文章 用于尝试回答这个问题:“为什么测试 Android 应用这么困难?”在这些帖子中,我提出是 Android 应用的标准架构使得测试如此困难的。
本文讲的是为什么 Android 测试如此困难:历史版本,

作为一种职业,程序员总是完全无视自己的历史。

David West, 《Object Thinking》

大约两年以前,我写了两篇文章 用于尝试回答这个问题:“为什么测试 Android 应用这么困难?”在这些帖子中,我提出是 Android 应用的标准架构使得测试如此困难的。这个对于 Android 应用测试困难性的解释提出了一个更深、更历史性的问题:为什么一个如此难以测试的架构,在当初会成为开发 Android 应用的默认方式?

在本帖子中,我将推测这个问题的答案。我认为 Android 目前不理想的测试状态由三个原因造成:性能因素、应用组件类目的不明确,以及在 Android 刚推出时 TDD 和自动化测试的不成熟。

性能

在某种程度上,代码的性能和可测试性是反相关的。就像 Michael Feathers 指出的那样,可测试的代码需要抽象层。

……遗留代码中一个普遍的问题是:它通常没有太多的抽象层;系统中最重要的代码通常和底层 API 调用混杂在一起。我们已经见到,它是怎样把测试复杂化的……[1]

如同 Chet Haase 所说,抽象层有性能代价。作为 Android 开发者,我们需要对其额外警惕:

如果有些代码很少执行……,但更清晰的风格对它有益,那么一个传统的抽象层会是正确的决定。但如果分析显示你经常反复执行某些代码路径,并在过程中造成大量内存抖动,考虑这些避免过量分配的策略……[2]

虽然 2017 年有“#perfmatters”,但性能问题在 Android 推出之初比现在更受关注。这意味着 Android API 的设计和 Android 应用的早期架构/实践是对性能非常敏感的。添加额外的抽象层用于测试,在那段时间可能是不现实的。

第一部 Android 手机,G1,有 192 MB RAM 和一个 528MHZ 的处理器。显然,从那以后我们已经走过了很长的路。而且在很多情况下,我们可以承受可测试性所要求的额外抽象层的代价。

我最近听 Ficus Kirkpatrick 说了一件有趣的事。它是关于在 Android 系统设计和早期的 Android 开发中,性能因素有多重要的。Ficus Kirkpatrick 是 Android 组成立时的成员之一,他在最近某期 Android Developers backstage 中提起:

…当涉及到 CPU 周期和内存时,就出现很多 enum 之类的东西和极度节俭的哲学……这是观察 Android 早期决定的一个有趣的角度。我看到很多工程师就像在大萧条时期长大的一样,锱铢必较地节俭。 [3]

关于性能和开发速度之间的权衡,在播客中已经有了很好的讨论。Chet Haase 和 Tor Norbye 非常强调性能因素,而目前在 Facebook 工作的 Ficus Fitzpatrick 看起来更倾向于牺牲性能换取开发速度。

谁是对的——或者意见是否最终可以达成一致——对我们不重要。重要的是他们的对话,和 关于 enums的宣传, 明确显示了 Android 系统的开发人员仍然很关心性能。这可能导致他们对于有一些性能消耗的抽象不那么热衷,哪怕这对测试有益。

关于 Android 组件的误解

另一个造成 Android 测试环境如此恶劣的原因是我们可能完全误解了 Android 的组件类(即ActivityService,BroadcastReceiver, 和 ContentProvider)的目的。在很长一段时间里,我以为这些类是用于方便应用开发的。Diane Hackborne 并不这样认为:

…从它的 Java 语言 API 和相当高层的概念来看,它像是一个典型的应用框架,用于指示应用应当如何工作。但就大部分情况而言,它不是。

大概把 Android API 称为“系统框架”会更合适。大多数情况下,我们提供的平台 API 是用于定义一个应用如何与操作系统互动的;但对于任何从纯粹在应用内部运行的东西而言,这些 API 和它并没有什么关系。

Chet Haase在他的 Developing for Android medium 博客中重新强调了这一点:

应用组件(activities, services, providers, receivers)是用于和操作系统互动的接口;不推荐把它们作为架构整个应用的核心。[4]

我认为现在大家都已经知道,把业务逻辑写在 Activity 和其它应用组件类中,会使测试变得困难 ,因为缺乏合适的依赖注入。由于我们中有许多人会围绕这些组件建立整个应用,我们可能会过度使用它们,使应用的测试状况进一步恶化。

Android 和单元测试的崛起

有另一件事情导致了 Android 不佳的测试状况: TDD 和 Android 同时崛起。 Android 最初的版本是在 2008 年九月发布的。最早的关于 TDD 型单元测试的书之一——TDD by Example,仅仅比它早 3 年。

自动化测试的重要性比那时更广泛地被接受。测试的重要性影响了 Android SDK 的设计决策,以及 Android 社区对于支持测试的架构和实践的热情。





原文发布时间为:2017年2月26日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
1月前
|
编解码 Android开发
Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)1
Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)
58 1
|
17天前
|
Linux Android开发
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
测试程序之提供ioctl函数应用操作GPIO适用于Linux/Android
13 0
|
1月前
|
编解码 开发工具 Android开发
Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)2
Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)2
35 2
|
2天前
|
Shell 开发工具 Android开发
android 修改kernel编译版本信息
android 修改kernel编译版本信息
5 0
|
2天前
|
存储 Android开发
Android 高版本 packageManager.getPackageArchiveInfo 总是返回null
Android 高版本 packageManager.getPackageArchiveInfo 总是返回null
10 1
|
16天前
|
网络协议 测试技术 网络性能优化
Android系统 以太网吞吐量和丢包测试
Android系统 以太网吞吐量和丢包测试
30 1
|
16天前
|
存储 应用服务中间件 网络安全
Android 网络链接稳定性测试解决方案
Android 网络链接稳定性测试解决方案
20 0
|
17天前
|
Linux 测试技术 Android开发
测试程序之UART 232/485适用于Android/Linux
测试程序之UART 232/485适用于Android/Linux
21 0
|
17天前
|
Java API Android开发
Android 11 修改libcore Cipher AS测试
Android 11 修改libcore Cipher AS测试
14 1
|
1月前
|
Android开发
Android Uri转File方法(适配android 10以上版本及android 10以下版本)
Android Uri转File方法(适配android 10以上版本及android 10以下版本)
26 0