Thư viện kiểm thử Health Connect (androidx.health.connect:connect-testing
) giúp đơn giản hoá việc tạo kiểm thử tự động. Bạn có thể dùng thư viện này để xác minh
hành vi của ứng dụng và xác thực xem ứng dụng có phản hồi chính xác với
các trường hợp không phổ biến, khó kiểm tra theo cách thủ công.
Bạn có thể sử dụng thư viện này để tạo kiểm thử đơn vị cục bộ. Thông thường, tính năng này sẽ xác minh hành vi của các lớp trong ứng dụng tương tác với ứng dụng Health Connect.
Để bắt đầu sử dụng thư viện, hãy thêm thư viện này làm phần phụ thuộc kiểm thử:
testImplementation("androidx.health.connect:connect-testing:1.0.0-alpha01")
Điểm truy cập vào thư viện là lớp FakeHealthConnectClient
mà bạn
sử dụng trong kiểm thử để thay thế HealthConnectClient
. FakeHealthConnectClient
có những tính năng sau:
- Biểu thị bản ghi trong bộ nhớ để bạn có thể chèn, xoá, xoá và đọc các bản ghi đó
- Tạo mã thông báo thay đổi và theo dõi thay đổi
- Phân trang cho bản ghi và thay đổi
- Phản hồi tổng hợp được hỗ trợ với các mã giả lập
- Cho phép mọi hàm gửi ngoại lệ
FakePermissionController
có thể dùng để mô phỏng hoạt động kiểm tra quyền
Để tìm hiểu thêm về cách thay thế phần phụ thuộc trong kiểm thử, hãy đọc bài viết Chèn phần phụ thuộc trong Android. Để biết thêm về đối tượng giả mạo, hãy đọc bài viết Sử dụng đối tượng kiểm thử trong Android.
Ví dụ: nếu lớp tương tác với ứng dụng được gọi
HealthConnectManager
và lấy HealthConnectClient
làm phần phụ thuộc
sẽ có dạng như sau:
class HealthConnectManager(
private val healthConnectClient: HealthConnectClient,
...
) { }
Trong kiểm thử, bạn có thể truyền một lớp giả đến lớp đang được kiểm thử:
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)
}
}
Kiểm thử này xác minh rằng hàm fetchReport
giả định trong
HealthConnectManager
lọc chính xác các bản ghi theo hoạt động.
Xác minh các trường hợp ngoại lệ
Hầu hết mọi lệnh gọi đến HealthConnectClient
đều có thể gửi ngoại lệ. Ví dụ: tài liệu về insertRecords
đề cập đến các ngoại lệ sau:
@throws android.os.RemoteException
cho mọi sự cố truyền tải IPC.@throws SecurityException
đối với các yêu cầu có quyền truy cập không được phép.@throws java.io.IOException
đối với mọi vấn đề về I/O trên ổ đĩa.
Những ngoại lệ này bao gồm các trường hợp như kết nối kém hoặc không còn khoảng trống trên thiết bị. Ứng dụng của bạn phải phản ứng chính xác với các vấn đề này trong thời gian chạy, vì chúng có thể xảy ra bất cứ lúc nào.
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)
}
Tổng hợp
Các lệnh gọi tổng hợp không có cách triển khai giả mạo. Thay vào đó, các lệnh gọi tổng hợp sử dụng các mã giả mà bạn có thể lập trình để hoạt động theo một cách nhất định. Bạn có thể truy cập vào các mã giả lập thông qua thuộc tính overrides
của FakeHealthConnectClient
.
Ví dụ: bạn có thể lập trình hàm tổng hợp để trả về một kết quả cụ thể:
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)
Sau đó, bạn có thể xác minh rằng lớp đang kiểm thử (HealthConnectManager
trong trường hợp này) đã xử lý kết quả chính xác:
// 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)
Quyền
Thư viện kiểm thử bao gồm một FakePermissionController
, có thể được truyền dưới dạng phần phụ thuộc đến FakeHealthConnectClient
.
Đối tượng kiểm thử của bạn có thể sử dụng PermissionController—through
thuộc tính permissionController
của giao diện HealthConnectClient
để kiểm tra quyền. Việc này thường được thực hiện trước mỗi lệnh gọi đến ứng dụng.
Để kiểm thử chức năng này, bạn có thể đặt quyền nào có sẵn bằng cách sử dụng 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)
}
Phân trang
Phân trang là một nguồn lỗi rất phổ biến, vì vậy, FakeHealthConnectClient
cung cấp các cơ chế giúp bạn xác minh rằng việc triển khai phân trang cho các bản ghi và thay đổi hoạt động đúng cách.
Đối tượng kiểm thử của bạn, HealthConnectManager
trong ví dụ của chúng tôi, có thể chỉ định kích thước trang trong ReadRecordsRequest
:
fun fetchRecordsReport(pageSize: Int = 1000) }
val pagedRequest =
ReadRecordsRequest(
timeRangeFilter = ...,
recordType = ...,
pageToken = page1.pageToken,
pageSize = pageSize,
)
val page = client.readRecords(pagedRequest)
...
Đặt kích thước trang ở một giá trị nhỏ, chẳng hạn như 2, cho phép bạn dễ dàng kiểm tra
phân trang. Ví dụ: bạn có thể chèn 5 bản ghi để readRecords
trả về 3 trang khác nhau:
@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)
}
Dữ liệu kiểm thử
Thư viện này chưa bao gồm các API để tạo dữ liệu giả, nhưng bạn có thể sử dụng dữ liệu và trình tạo mà thư viện sử dụng trong Android Code Search.
Đoạn mã tạm thời
Thuộc tính overrides
của FakeHealthConnectClient
cho phép bạn lập trình (hoặc gỡ lỗi) bất kỳ hàm nào của thuộc tính này để các hàm đó gửi ngoại lệ khi được gọi.
Các lệnh gọi tổng hợp cũng có thể trả về dữ liệu tuỳ ý và hỗ trợ tính năng thêm vào hàng đợi
nhiều câu trả lời. Hãy xem Stub
và MutableStub
để biết thêm thông tin.
Tóm tắt các trường hợp hiếm gặp
- Xác minh rằng ứng dụng của bạn hoạt động như dự kiến khi ứng dụng gửi ra các ngoại lệ. Hãy kiểm tra tài liệu của từng hàm để tìm hiểu những ngoại lệ bạn nên kiểm tra.
- Xác minh rằng mỗi cuộc gọi bạn thực hiện đến khách hàng đều được bắt đầu bằng kiểm tra quyền.
- Xác minh cách triển khai tính năng phân trang.
- Xác minh điều gì xảy ra khi bạn tìm nạp nhiều trang nhưng có một trang đã hết hạn mã thông báo.