FakeHealthConnectClient


@ExperimentalTestingApi
class FakeHealthConnectClient : HealthConnectClient


Fake HealthConnectClient to be used in tests for components that use it as a dependency.

Features:

Note that this fake does not check for permissions.

Summary

Constants

const String

Default package name used in FakeHealthConnectClient.

Public constructors

FakeHealthConnectClient(
    packageName: String,
    clock: Clock,
    permissionController: PermissionController
)

Public functions

open suspend AggregationResult
open suspend List<AggregationResultGroupedByDuration>
open suspend List<AggregationResultGroupedByPeriod>
open suspend Unit
deleteRecords(
    recordType: KClass<Record>,
    timeRangeFilter: TimeRangeFilter
)

Deletes any Record of the given recordType in the given timeRangeFilter (automatically filtered to Record belonging to the calling application).

open suspend Unit
deleteRecords(
    recordType: KClass<Record>,
    recordIdsList: List<String>,
    clientRecordIdsList: List<String>
)

Deletes one or more Record by their identifiers.

Unit

Set a particular token as expired.

open suspend ChangesResponse
getChanges(changesToken: String)

Retrieves changes in Android Health Platform, from a specific point in time represented by provided changesToken.

open suspend String

Returns a fake token which contains the key to the next change.

open suspend InsertRecordsResponse

Fake implementation that inserts one or more Records into the in-memory store.

open suspend ReadRecordResponse<T>
<T : Record> readRecord(recordType: KClass<T>, recordId: String)

Reads one Record point with its recordType and recordId.

open suspend ReadRecordsResponse<T>
<T : Record> readRecords(request: ReadRecordsRequest<T>)

Returns records that match the attributes in a ReadRecordsRequest.

open suspend Unit

Updates one or more Record of given UIDs to newly specified values.

Public properties

open HealthConnectFeatures

Access operations related to feature availability.

FakeHealthConnectClientOverrides

Used to override or intercept responses to emulate scenarios that this fake doesn't support.

Int

Overrides the page size to test pagination when calling getChanges.

open PermissionController

grants and revokes permissions.

Constants

DEFAULT_PACKAGE_NAME

const val DEFAULT_PACKAGE_NAMEString

Default package name used in FakeHealthConnectClient.

Public constructors

FakeHealthConnectClient

Added in 1.0.0-alpha01
FakeHealthConnectClient(
    packageName: String = DEFAULT_PACKAGE_NAME,
    clock: Clock = Clock.systemDefaultZone(),
    permissionController: PermissionController = FakePermissionController()
)
Parameters
packageName: String = DEFAULT_PACKAGE_NAME

the name of the package to use to generate unique record IDs.

clock: Clock = Clock.systemDefaultZone()

used to close open-ended TimeRangeFilters and record update times.

permissionController: PermissionController = FakePermissionController()

grants and revokes permissions.

Public functions

aggregate

open suspend fun aggregate(request: AggregateRequest): AggregationResult
Throws
kotlin.IllegalStateException

if no overrides are configured.

aggregateGroupByDuration

open suspend fun aggregateGroupByDuration(request: AggregateGroupByDurationRequest): List<AggregationResultGroupedByDuration>
Throws
kotlin.IllegalStateException

if no overrides are configured.

aggregateGroupByPeriod

open suspend fun aggregateGroupByPeriod(request: AggregateGroupByPeriodRequest): List<AggregationResultGroupedByPeriod>
Throws
kotlin.IllegalStateException

if no overrides are configured.

deleteRecords

Added in 1.0.0-alpha01
open suspend fun deleteRecords(
    recordType: KClass<Record>,
    timeRangeFilter: TimeRangeFilter
): Unit

Deletes any Record of the given recordType in the given timeRangeFilter (automatically filtered to Record belonging to the calling application). Deletion of multiple Record is executed in a transaction - if one fails, none is deleted.

import androidx.health.connect.client.deleteRecords
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.time.TimeRangeFilter

healthConnectClient.deleteRecords<StepsRecord>(
    timeRangeFilter = TimeRangeFilter.between(startTime, endTime)
)
Parameters
recordType: KClass<Record>

Which type of Record to delete, such as StepsRecord::class

timeRangeFilter: TimeRangeFilter

The TimeRangeFilter to delete from

Throws
android.os.RemoteException

For any IPC transportation failures.

java.lang.SecurityException

For requests with unpermitted access.

java.io.IOException

For any disk I/O issues.

Example usage to delete written steps data in a time range:

deleteRecords

Added in 1.0.0-alpha01
open suspend fun deleteRecords(
    recordType: KClass<Record>,
    recordIdsList: List<String>,
    clientRecordIdsList: List<String>
): Unit

Deletes one or more Record by their identifiers. Deletion of multiple Record is executed in single transaction - if one fails, none is deleted.

import androidx.health.connect.client.deleteRecords
import androidx.health.connect.client.records.StepsRecord

healthConnectClient.deleteRecords<StepsRecord>(
    recordIdsList = listOf(uid1, uid2),
    clientRecordIdsList = emptyList()
)
Parameters
recordType: KClass<Record>

Which type of Record to delete, such as Steps::class

recordIdsList: List<String>

List of androidx.health.connect.client.records.metadata.Metadata.id of Record to delete

clientRecordIdsList: List<String>

List of client record IDs of Record to delete

Throws
android.os.RemoteException

For any IPC transportation failures. Deleting by invalid identifiers such as a non-existing identifier or deleting the same record multiple times will result in IPC failure.

java.lang.SecurityException

For requests with unpermitted access.

java.io.IOException

For any disk I/O issues.

Example usage to delete written steps data by its unique identifier:

expireToken

Added in 1.0.0-alpha01
fun expireToken(token: String): Unit

Set a particular token as expired. This is used to test the response of getChanges.

getChanges

open suspend fun getChanges(changesToken: String): ChangesResponse

Retrieves changes in Android Health Platform, from a specific point in time represented by provided changesToken.

The response returned may not provide all the changes due to IPC or memory limits, see ChangesResponse.hasMore. Clients can make more api calls to fetch more changes from the Android Health Platform with updated ChangesResponse.nextChangesToken.

Provided changesToken may have expired if clients have not synced for extended period of time (such as a month). In this case ChangesResponse.changesTokenExpired will be set, and clients should generate a new changes-token via getChangesToken.

val response = client.getChanges(changesToken)
if (response.changesTokenExpired) {
// Consider re-sync and fetch new changes token.
} else {
// Process new insertion/deletions, either update local storage or upload to backends.
}
Parameters
changesToken: String

A Changes-Token that represents a specific point in time in Android Health Platform.

Returns
ChangesResponse

a ChangesResponse with changes since provided changesToken.

Throws
android.os.RemoteException

For any IPC transportation failures.

java.lang.SecurityException

For requests with unpermitted access.

See also
getChangesToken

getChangesToken

open suspend fun getChangesToken(request: ChangesTokenRequest): String

Returns a fake token which contains the key to the next change. Used with getChanges to track changes from the moment this function is called.

insertRecords

open suspend fun insertRecords(records: List<Record>): InsertRecordsResponse

Fake implementation that inserts one or more Records into the in-memory store.

Supports deduplication of androidx.health.connect.client.records.metadata.Metadata.clientRecordIds using androidx.health.connect.client.records.metadata.Metadata.clientRecordVersion to determine precedence.

readRecord

open suspend fun <T : Record> readRecord(recordType: KClass<T>, recordId: String): ReadRecordResponse<T>

Reads one Record point with its recordType and recordId.

Parameters
recordType: KClass<T>

Which type of Record to read, such as Steps::class

recordId: String

androidx.health.connect.client.records.metadata.Metadata.id of Record to read

Returns
ReadRecordResponse<T>

The Record data point.

Throws
android.os.RemoteException

For any IPC transportation failures. Update with invalid identifiers will result in IPC failure.

java.lang.SecurityException

For requests with unpermitted access.

java.io.IOException

For any disk I/O issues.

readRecords

open suspend fun <T : Record> readRecords(request: ReadRecordsRequest<T>): ReadRecordsResponse<T>

Returns records that match the attributes in a ReadRecordsRequest.

Features a simple paging implementation. Records must not be updated in between calls.

Throws
kotlin.IllegalStateException

if paging is requested.

updateRecords

open suspend fun updateRecords(records: List<Record>): Unit

Updates one or more Record of given UIDs to newly specified values. Update of multiple records is executed in a transaction - if one fails, none is inserted.

Parameters
records: List<Record>

List of records to update

Throws
android.os.RemoteException

For any IPC transportation failures. Update with invalid identifiers will result in IPC failure.

java.lang.SecurityException

For requests with unpermitted access.

java.io.IOException

For any disk I/O issues.

Public properties

features

open val featuresHealthConnectFeatures

Access operations related to feature availability.

overrides

Added in 1.0.0-alpha01
val overridesFakeHealthConnectClientOverrides

Used to override or intercept responses to emulate scenarios that this fake doesn't support.

Every call in FakeHealthConnectClient can be overridden.

For example:

import androidx.health.connect.client.testing.stubs.stub

// Sets a default return value for client.aggregate() and a queue with one item.
client.overrides.aggregate = stub(queue = listOf(resultOnce)) { result }
import androidx.health.connect.client.aggregate.AggregationResult
import androidx.health.connect.client.testing.AggregationResult
import androidx.health.connect.client.testing.stubs.MutableStub
import androidx.health.connect.client.testing.stubs.enqueue

// Sets a default exception that will be thrown for client.aggregate().
val aggregationStub = MutableStub<AggregationResult>(exception)
client.overrides.aggregate = aggregationStub

// Only the first call to client.aggregate() will return this result. Subsequent calls will
// throw the default.
aggregationStub.enqueue(result)

// Setting a default response removes the default exception.
aggregationStub.defaultHandler = { AggregationResult() }
import androidx.health.connect.client.aggregate.AggregationResult
import androidx.health.connect.client.records.ExerciseSessionRecord
import androidx.health.connect.client.records.HeartRateRecord
import androidx.health.connect.client.testing.AggregationResult
import androidx.health.connect.client.testing.stubs.stub

val result =
    AggregationResult(
        metrics =
            buildMap {
                put(HeartRateRecord.BPM_AVG, 74.0)
                put(ExerciseSessionRecord.EXERCISE_DURATION_TOTAL, Duration.ofMinutes(30))
            }
    )
client.overrides.aggregate = stub(result)
import androidx.health.connect.client.aggregate.AggregationResult
import androidx.health.connect.client.aggregate.AggregationResultGroupedByDuration
import androidx.health.connect.client.testing.AggregationResult
import androidx.health.connect.client.testing.stubs.stub

val result = buildList {
    add(
        AggregationResultGroupedByDuration(
            aggregationResult1,
            startTime1,
            endTime1,
            ZoneOffset.UTC
        )
    )
    add(
        AggregationResultGroupedByDuration(
            aggregationResult2,
            startTime2,
            endTime2,
            ZoneOffset.UTC
        )
    )
}
client.overrides.aggregateGroupByDuration = stub(default = result)
import androidx.health.connect.client.aggregate.AggregationResult
import androidx.health.connect.client.aggregate.AggregationResultGroupedByPeriod
import androidx.health.connect.client.testing.AggregationResult
import androidx.health.connect.client.testing.stubs.stub

val result = buildList {
    add(AggregationResultGroupedByPeriod(aggregationResult1, startTime1, endTime1))
    add(AggregationResultGroupedByPeriod(aggregationResult2, startTime2, endTime2))
}
client.overrides.aggregateGroupByPeriod = stub(default = result)

pageSizeGetChanges

Added in 1.0.0-alpha01
var pageSizeGetChangesInt

Overrides the page size to test pagination when calling getChanges.

This is typically used with a low number (such as 2) so that a low number of inserted records (such as 3) generate multiple pages. Use it to test token expiration as well.

permissionController

Added in 1.0.0-alpha01
open val permissionControllerPermissionController

grants and revokes permissions.