一个 空闲资源 表示一项异步操作,其结果会影响界面测试中的后续操作。通过在 Espresso 中注册空闲资源,您可以在测试应用时更可靠地验证这些异步操作。
确定何时需要空闲资源
Espresso 提供一套完善的同步功能。不过,此框架特性仅适用于在 MessageQueue
上发布消息的操作,例如在屏幕上绘制内容的 View
子类。
由于 Espresso 不知道任何其他异步操作(包括在后台线程中运行的操作),因此 Espresso 无法在这些情况下提供其同步保证。为了让 Espresso 了解您应用中长时间运行的操作,您必须将每个操作都注册为空闲资源。
如果您在测试应用的异步工作结果时没有使用空闲资源,您可能会发现自己不得不使用以下糟糕的替代方法来提高测试的可靠性:
- 添加对
Thread.sleep()
的调用。 当您在测试中添加人工延迟时,测试套件需要更长时间才能完成执行,并且在较慢的设备上执行时,您的测试有时仍可能会失败。此外,这些延迟的可伸缩性不佳,因为您的应用在将来的版本中可能需要执行更耗时的异步工作。 - 实现重试封装容器, 这些容器使用循环重复检查您的应用是否仍在执行异步工作,直到发生超时。即使您在测试中指定了最大重试次数,每次重新执行都会消耗系统资源,尤其是 CPU。
- 使用
CountDownLatch
实例, 这些实例允许一个或多个线程等待,直到另一个线程中执行的特定数量的操作完成。这些对象要求您指定超时时长;否则,您的应用可能会无限期地被阻塞。这些锁存器还会增加代码中不必要的复杂性,使维护更加困难。
Espresso 允许您从测试中移除这些不可靠的替代方法,并改为将应用的异步工作注册为空闲资源。
常见用例
在测试中执行与以下示例类似的操作时,请考虑使用空闲资源:
- 从互联网或本地数据源加载数据。
- 与数据库和回调建立连接。
- 管理服务,可以使用系统服务或
IntentService
实例。 - 执行复杂的业务逻辑,例如位图转换。
当这些操作更新您的测试随后要验证的界面时,注册空闲资源尤为重要。
空闲资源实现示例
以下列表介绍了可以集成到您的应用中的几种空闲资源实现示例:
CountingIdlingResource
- 维护活跃任务的计数器。当计数器为零时,关联的资源被视为空闲。此功能与
Semaphore
的功能非常相似。在大多数情况下,此实现足以在测试期间管理您应用的异步工作。 UriIdlingResource
- 类似于
CountingIdlingResource
,但计数器需要在特定时间段内保持为零,然后资源才被视为空闲。这个额外的等待时间考虑了连续的网络请求,即您的线程中的应用可能在收到对上一个请求的响应后立即发出新请求。 IdlingThreadPoolExecutor
- 是
ThreadPoolExecutor
的自定义实现,用于跟踪所创建线程池中运行任务的总数。此类使用CountingIdlingResource
来维护活跃任务的计数器。 IdlingScheduledThreadPoolExecutor
- 是
ScheduledThreadPoolExecutor
的自定义实现。它提供与IdlingThreadPoolExecutor
类相同的功能和能力,但它还可以跟踪将来计划执行或计划定期执行的任务。
创建您自己的空闲资源
在您的应用测试中使用空闲资源时,您可能需要提供自定义资源管理或日志记录。在这些情况下,上一节中列出的实现可能不足。如果出现这种情况,您可以扩展这些空闲资源实现之一或创建您自己的实现。
如果您实现自己的空闲资源功能,请牢记以下最佳实践,特别是第一条:
- 在空闲检查之外调用向空闲状态的转换。
- 应用空闲后,在
isIdleNow()
的任何实现之外调用onTransitionToIdle()
。这样,Espresso 就不会进行第二次不必要的检查来确定给定空闲资源是否空闲。
以下代码段说明了此建议:
Kotlin
fun isIdle() { // DON'T call callback.onTransitionToIdle() here! } fun backgroundWorkDone() { // Background work finished. callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle. // Don't do any post-processing work beyond this point. Espresso now // considers your app to be idle and moves on to the next test action. }
Java
public void isIdle() { // DON'T call callback.onTransitionToIdle() here! } public void backgroundWorkDone() { // Background work finished. callback.onTransitionToIdle() // Good. Tells Espresso that the app is idle. // Don't do any post-processing work beyond this point. Espresso now // considers your app to be idle and moves on to the next test action. }
- 在需要空闲资源之前注册它们。
与空闲资源相关的同步优势仅在 Espresso 首次调用该资源的
isIdleNow()
方法后才会生效。以下列表显示了此属性的几个示例:
- 如果您在使用
@Before
注解的方法中注册空闲资源,则空闲资源会在每个测试的第一行生效。 - 如果您在测试中注册空闲资源,则空闲资源会在下一个基于 Espresso 的操作期间生效。即使下一个操作与注册空闲资源的语句位于同一个测试中,此行为也会发生。
- 如果您在使用
- 使用完空闲资源后,请注销它们。
为了节省系统资源,您应该在不再需要空闲资源时立即注销它们。例如,如果您在带有
@Before
注解的方法中注册空闲资源,最好在带有@After
注解的相应方法中注销此资源。- 使用空闲注册表注册和注销空闲资源。
通过将此容器用于您应用的空闲资源,您可以根据需要重复注册和注销空闲资源,并且仍然观察到一致的行为。
- 在空闲资源中仅维护简单的应用状态。
例如,您实现和注册的空闲资源不应包含对
View
对象的引用。
注册空闲资源
Espresso 提供了一个容器类,您可以将应用的空闲资源放入其中。此类名为 IdlingRegistry
,是一个自包含的工件,对您的应用引入的开销极小。该类还允许您采取以下步骤来提高应用的可维护性:
- 在您的应用测试中创建对
IdlingRegistry
的引用,而不是对其包含的空闲资源的引用。 - 维护您用于每个构建变体的空闲资源集合中的差异。
- 在您应用的服务中定义空闲资源,而不是在引用这些服务的界面组件中定义。
将空闲资源集成到您的应用中
虽然您可以通过多种不同的方式将空闲资源添加到应用中,但有一种方法特别能够保持应用的封装性,同时仍然允许您指定给定空闲资源所代表的特定操作。
推荐方法
在将空闲资源添加到您的应用中时,我们强烈建议将空闲资源逻辑放在应用本身中,并且只在测试中执行注册和注销操作。
尽管采用这种方法会在生产代码中创建使用仅用于测试的接口的异常情况,但您可以将空闲资源封装到您已有的代码中,从而保持应用的 APK 大小和方法计数。
替代方法
如果您希望在应用的生产代码中不包含空闲资源逻辑,还有其他几种可行的集成策略:
- 创建构建变体(例如 Gradle 的产品变种),并且只在应用的调试构建中使用空闲资源。
- 使用像 Dagger 这样的依赖注入框架,将您应用的空闲资源依赖项图注入到您的测试中。如果您使用的是 Dagger 2,则注入本身应源自子组件。
在您的应用测试中实现一个空闲资源,并公开应用实现中需要在这些测试中同步的部分。
注意: 尽管此设计决策似乎创建了对空闲资源的自包含引用,但它在除最简单的应用之外的所有应用中都会破坏封装性。
其他资源
如需详细了解如何在 Android 测试中使用 Espresso,请参阅以下资源。
示例
- IdlingResourceSample:与后台作业同步。