ヘルスコネクト テスト ライブラリを使用して単体テストを作成する

ヘルスコネクト テスト ライブラリ(androidx.health.connect:connect-testing) 自動テストの作成を簡素化できます。このライブラリを使用して、 適切に応答することを検証し、 手動でのテストは困難です。

このライブラリを使用して、ローカル単体テストを作成できます。通常、このテストは、 ヘルスコネクトとやり取りするアプリ内のクラスの動作 できます

ライブラリの使用を開始するには、テストの依存関係として追加します。

 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)
}

集計

集約呼び出しに偽の実装はありません。代わりに、集計関数を使用して スタブを使用して、特定の動作をするようにプログラムできます。こちらの スタブに FakeHealthConnectClientoverrides プロパティを使用。

たとえば、特定の結果を返すように集計関数をプログラムできます。

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)
}

ページ分け

ページ分けはバグが発生する一般的な原因であるため、FakeHealthConnectClient には、ページングの実装が Google Cloud 内で 正しく機能することを確認します。

テスト対象(この例では HealthConnectManager)は、 ReadRecordsRequest のページサイズ:

fun fetchRecordsReport(pageSize: Int = 1000) }
    val pagedRequest
=
       
ReadRecordsRequest(
            timeRangeFilter
= ...,
            recordType
= ...,
            pageToken
= page1.pageToken,
            pageSize
= pageSize,
       
)
    val page
= client.readRecords(pagedRequest)
   
...

ページサイズを小さな値(2 など)に設定すると、 ページ分け。たとえば、readRecords が 3 を返すように 5 つのレコードを挿入できます。 ページごとに異なります:

@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 ソースコード検索のライブラリで使用されるデータとジェネレータ。

スタブ

FakeHealthConnectClientoverrides プロパティを使用すると、 スタブアウト)を使用して、例外をスローするようにします。 集計呼び出しでは任意のデータを返すこともでき、 複数回答が返されます詳細については、StubMutableStub をご覧ください。

エッジケースの概要

  • クライアントが例外をスローしたときに、アプリが期待どおりに動作することを確認します。 各関数のドキュメントを参照して、どの例外が例外として使用されるかについては、 確認します。
  • クライアントに対して行うすべての呼び出しの前に、必ず適切な 許可チェックを行います。
  • ページ分けの実装を確認します。
  • 複数のページを取得したときに 1 つの有効期限が切れている場合 あります。