[Test apps on Android] Fundamentals of Testing
本文是翻译的Test apps on Android的官方文档 Fundamentals of Testing
本文不照搬每一个单词,理解有误请跳转原文链接。
Testing
Testing是App开发必需要做的部分,通过持续性的运行tests,可以在发布之前验证App的正确性,比如功能行为和可用性。除了这些,还有其它的好处:
- 快速的对线上问题进行反馈
- 在开发的时候尽早的发现问题
- 安全的进行代码重构,无需担回滚代码,就可以放心优化代码。
- 保持稳定的开发进度,帮助你减少技术债务。
Fundamentals of Testing
在迭代开发App的时候,应该要测试各种 use case 和 interactions.
Organize your code for testing
随着App的扩展,你会发现有必要从服务器获取数据,与设备的传感器进行交互,访问本地数据库以及呈现复杂的UI界面。
所以,一个App需要有全面的测试策略。
Create and test code iteratively
在一个功能迭代周期中,可以从编写新的测试或者向现有的单元测试中添加案例和断言开着手,但是这些测试会因为功能没有真正实现而失败,这是正常的。
在设计一个新功能时,要分析出有哪些单元,对于每个单元,都要编写对应的测试,而且这些测试要考虑到所有可能的交互,包括正常的交互、无效输入以及资源不可用等情况。
在编写测试时,尽量使用JetPack库,使用这些经过良好测试的库,你会很省心,从而更能专注于编写测试的专属行为。
与测试驱动的迭代开发相关的两个周期,tests-workflow.
这是一个嵌套的迭代周期,会一直持续直到覆盖了每个use case为止:
- 长而缓慢的,是UI-driven cycle,是一次代码单元的集成测试。
- 短而快的,是单元测试。
View your app as a series of modules
为了使得代码易于测试,你的代码需要用模块来划分,每个模块代表用户在你的App上完成的特定任务。这个划分的方式和stack-based层级视图不是一个概念,stack-based是按照app UI,business logic和data层来划分。
比如,一个 TODO App,它有创建Task,查看Task Detail相关等的这样一些模块,这样的模块化体系结构,有利于团队分配任务。
每个模块都需要有明确的边界,都只需要关注一个特定的任务。
Configure your test environment
配置环境和依赖关系以便于在应用程序中创建测试,请遵循本节介绍的最佳做法。
基于运行环境配置测试目录
在Android Studio中通常包含2个目录来放置你的测试:
-
androidTest
该目录放置依赖于真实设备或者虚拟设备运行的测试,这类测试包括integration tests, end-to-end tests,还有仅依赖JVM不能运行的其它测试。 -
test
该目录放置一些本地运行的测试,比如 unit test。
基于不同类型设备的权衡
运行依赖设备的测试时,有以下类型:
- 真实设备,提供最高的保真度,测试运行花费的时间也最多。
- 仿真设备,例如Robolectric,以保真度较低为代价提供了改进的测试运行速度。
- 虚拟设备,例如Android Studio 模拟器,在保真度和运行速度之间进行了权衡。
权衡使用真实对象还是模拟对象进行测试
通常来说,在测试的时候使用真实的对象比使用模拟的对象好,尤其是以下情况:
- The object is a data object.
- The object function dependency a real object, for example, event callback handler.
- 很难模拟具有reference关系的对象,比如Room,其内存中的数据库比mock的数据库具有更好的可测试性。
特别要注意的是,对不是你编写的类对象进行mock通常导致测试会非常脆弱,因为你根本不了解其他人实现此类的复杂性,迫不得已不要这样做。
但是mock自己写的类对象是可以的,只是注意使用spy
提供的对象比mock
具有更多的保真度。
而且如果你的测试是尝试对对象执行以下操作时,最好创建mock的对象:
- 执行耗时操作, 比如处理大文件。
- Non-hermetic actions, such as connecting to an arbitrary open port.
- 难以创建的配置型对象。
Write your tests
读应用程序进行功能评估之后,开始编写测试。
本节介绍如何编写small, medium, and large tests.
Levels of the Testing Pyramid
The Testing Pyramid:
- Small tests,是单元测试。
- Medium tests,是integration tests,集成测试。
- Large tests,是end-to-end tests,测试用户的一次特定完整行为。
占比: 70 percent small, 20 percent medium, and 10 percent large.
Write small tests
编写的 small tests 应该是高度内聚的单元测试,可以验证应用程序中每个类的功能。
在特定的类进行新增和更改方法时,需要针对这些类创建和运行单元测试。
如果这些测试依赖于Android framwork,请统一使用于设备无关的API,例如androidx.test.*
,这些API可以使你的测试无需物理设备或者仿真设备即可在本地环境运行。
Local unit tests
尽可能的使用AndroidX Test API,以便于编写的单元测试在需要的时候可以在设备或则模拟器上运行。
对于那些只在本地JVM环境运行的测试,可以使用Robolectric。
Robolectric模拟的是Android 4.1 或则更高版本的运行时,并且可以提供由社区维护的fake object而不是mock object,这中做法叫 shadow。
Instrumented unit tests
仪器化的单元测试,需要依赖于物理设备或者模拟器,相比于local unit tests,它较为耗时。
在运行Instrumented unit tests时,AndroidX Test将会使提供如下的2个线程:
-
main thread,如果你想在这个线程运行测试,使用
@UiThreadTest
注解标记,一般试在测试UI交互或者Activity生命周期时。 - instrumentation thread,大多数情况下Instrumented unit tests是在这个线程运行。
Compare unit test and instrumentation test
Write medium tests
a group of units.
Define the best way of the medium:
- 视图和视图模型之间的交互,比如测试Fragment,验证布局XML或者评估ViewModel的数据绑定逻辑。
- 在应用程序的存储层进行测试,验证不同的数据源和数据访问对象是否按预期进行交互。
- 在stack-based layer进行测试,验证个层之间的交互是否按照预期。
- Multi-fragment tests,该测试略有不同,需要依赖物理设备。
通常需要用到Espresso。
Use Espresso when running instrumented medium tests。
Write large tests
验证可指导用户完成跨越多个模块和功能的工作流程也是很重要的。
编写这种类型的测试不可避免的会遇到一些困难,但是可以验证尽可能接近实际应用程序的测试来达到目的。
????Note:
- 对于编写大型的,基于工作流程的测试,首先应该编写中型的测试,以检查工作流程中的每个模块的功能,这种测试结构使得,确定用户的哪一步操作表现出意外行为更加容易。
如果您的应用足够小,则可能只需要一组大型测试即可评估应用的整体功能。
Complete other testing tasks using AndroidX Test
本节介绍如何使用AndroidX Test进一步完善你的测试。
Create more readable assertions using Truth
Truth - Fluent assertions for Java and Android。
AndroidX Test对Truth这个断言库提供了很好的支持,以使基于Truth的断言更容易构建。
Write UI tests
Espresso allows you to programmatically locate and interact with UI elements in your app in a thread-safe way.
Run UI tests
The AndroidJUnitRunner class defines an instrumentation-based JUnit test runner that lets you run JUnit 3- or JUnit 4-style test classes on Android devices.
Interact with visible elements
The UI Automator API lets you interact with visible elements on a device, regardless of the activity or fragment that has focus.
基本就这些了,原文跳转链接????。