Health Connect Testing 库 (androidx.health.connect:connect-testing
)
简化了自动化测试的创建过程您可以使用此库来验证
以及确认应用能够正确响应
不常见的情况,很难手动测试。
您可以使用该库创建本地单元测试,这通常用于验证 应用中与 Health Connect 交互的类的行为 客户端。
如需开始使用该库,请将其添加为测试依赖项:
testImplementation("androidx.health.connect:connect-testing:1.0.0-alpha01")
该库的入口点是 FakeHealthConnectClient
类,
用于替换 HealthConnectClient
。FakeHealthConnectClient
具有以下功能:
- 记录在内存中的表示形式,便于插入、移除、删除和 读出来
- 生成更改令牌和更改跟踪
- 记录和更改的分页
- 通过桩支持汇总响应
- 允许任何函数抛出异常
- 可用于模拟权限检查的
FakePermissionController
如需详细了解如何在测试中替换依赖项,请参阅 Android 中的依赖项注入。有关仿冒产品的更多信息,请阅读 在 Android 中使用测试替身。
例如,如果与客户端交互的类调用
HealthConnectManager
,它接受 HealthConnectClient
作为依赖项,
如下所示:
class HealthConnectManager(
private val healthConnectClient: HealthConnectClient,
...
) { }
在测试中,您可以改为将虚假对象传递给被测类:
import androidx.health.connect.client.testing.ExperimentalTestingApi
import androidx.health.connect.client.testing.FakeHealthConnectClient
import kotlinx.coroutines.test.runTest
@OptIn(ExperimentalTestingApi::class)
class HealthConnectManagerTest {
@Test
fun readRecords_filterByActivity() = runTest {
// Create a Fake with 2 running records.
val fake = FakeHealthConnectClient()
fake.insertRecords(listOf(fakeRunRecord1, fakeBikeRecord1))
// Create a manager that depends on the fake.
val manager = HealthConnectManager(fake)
// Read running records only.
val runningRecords = manager.fetchReport(activity = Running)
// Verify that the records were filtered correctly.
assertTrue(runningRecords.size == 1)
}
}
此测试用于验证以下情况中的虚构 fetchReport
函数:
HealthConnectManager
按活动正确过滤记录。
验证异常
几乎每次调用 HealthConnectClient
都会抛出异常。例如:
insertRecords
的文档提到这些异常:
- 如果发生任何 IPC 传输失败,则为
@throws android.os.RemoteException
。 @throws SecurityException
(针对具有未允许访问权限的请求)。- 如果有任何磁盘 I/O 问题,则为
@throws java.io.IOException
。
这些例外情况包括连接状况不佳或 设备。您的应用必须对这些运行时问题做出正确响应,因为它们 随时可能发生
import androidx.health.connect.client.testing.stubs.stub
@Test
fun addRecords_throwsRemoteException_errorIsExposed() {
// Create Fake that throws a RemoteException
// when insertRecords is called.
val fake = FakeHealthConnectClient()
fake.overrides.insertRecords = stub { throw RemoteException() }
// Create a manager that depends on the fake.
val manager = HealthConnectManager(fake)
// Insert a record.
manager.addRecords(fakeRunRecord1)
// Verify that the manager is exposing an error.
assertTrue(manager.errors.size == 1)
}
集合
汇总调用没有虚假实现。相反,聚合调用
使用可通过编程实现特定行为的桩。您可以访问
通过 FakeHealthConnectClient
的 overrides
属性来创建桩。
例如,您可以对聚合函数进行编程,使其返回特定结果:
import androidx.health.connect.client.testing.AggregationResult
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.records.ExerciseSessionRecord
import java.time.Duration
@Test
fun aggregate() {
// Create a fake result.
val result =
AggregationResult(metrics =
buildMap {
put(HeartRateRecord.BPM_AVG, 74.0)
put(
ExerciseSessionRecord.EXERCISE_DURATION_TOTAL,
Duration.ofMinutes(30)
)
}
)
// Create a fake that always returns the fake
// result when aggregate() is called.
val fake = FakeHealthConnectClient()
fake.overrides.aggregate = stub(result)
然后,您可以在以下代码中验证被测类 HealthConnectManager
正确处理了结果:
// Create a manager that depends on the fake.
val manager = HealthConnectManager(fake)
// Call the function that in turn calls aggregate on the client.
val report = manager.getHeartRateReport()
// Verify that the manager is exposing an error.
assertThat(report.bpmAverage).isEqualTo(74.0)
权限
测试库包含一个 FakePermissionController
,可传递
作为对 FakeHealthConnectClient
的依赖项。
被测对象可以使用 PermissionController—through
HealthConnectClient
接口的 permissionController
属性 - 用于检查
权限。此操作通常在每次调用客户端之前完成。
如需测试此功能,您可以使用
FakePermissionController
:
import androidx.health.connect.client.testing.FakePermissionController
@Test
fun newRecords_noPermissions_errorIsExposed() {
// Create a permission controller with no permissions.
val permissionController = FakePermissionController(grantAll = false)
// Create a fake client with the permission controller.
val fake = FakeHealthConnectClient(permissionController = permissionController)
// Create a manager that depends on the fake.
val manager = HealthConnectManager(fake)
// Call addRecords so that the permission check is made.
manager.addRecords(fakeRunRecord1)
// Verify that the manager is exposing an error.
assertThat(manager.errors).hasSize(1)
}
分页
分页是很常见的 bug 来源,因此 FakeHealthConnectClient
提供了各种机制来帮助您验证
是否正常运行
被测对象(在本例中为 HealthConnectManager
)可以指定
ReadRecordsRequest
中的页面大小:
fun fetchRecordsReport(pageSize: Int = 1000) }
val pagedRequest =
ReadRecordsRequest(
timeRangeFilter = ...,
recordType = ...,
pageToken = page1.pageToken,
pageSize = pageSize,
)
val page = client.readRecords(pagedRequest)
...
将页面大小设置为较小的值(例如 2)可让您轻松测试
。例如,您可以插入 5 条记录,以便 readRecords
返回 3
不同页面:
@Test
fun readRecords_multiplePages() = runTest {
// Create a Fake with 2 running records.
val fake = FakeHealthConnectClient()
fake.insertRecords(generateRunningRecords(5))
// Create a manager that depends on the fake.
val manager = HealthConnectManager(fake)
// Read records with a page size of 2.
val report = manager.generateReport(pageSize = 2)
// Verify that all the pages were processed correctly.
assertTrue(report.records.size == 5)
}
测试数据
该库尚不包含用于生成虚假数据的 API,但您可以使用 Android 代码搜索中库使用的数据和生成器。
桩
通过 FakeHealthConnectClient
的 overrides
属性,您可以编程(或
打桩)其任何函数,以便它们在调用时抛出异常。
聚合调用也可以返回任意数据,并且支持加入队列
生成多份回答。如需了解详情,请参阅 Stub
和 MutableStub
。
极端情况摘要
- 验证在客户端抛出异常时应用的行为是否符合预期。 请查看每个函数的文档,了解 。
- 确保您对客户的每次调用都以正确的 权限检查。
- 验证分页实现。
- 验证当您提取多个网页但其中一个网页已过期时会发生什么情况 令牌。