FakeHealthConnectClient


@ExperimentalTestingApi
public final class FakeHealthConnectClient implements 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

static final @NonNull String

Default package name used in FakeHealthConnectClient.

Public constructors

FakeHealthConnectClient(
    @NonNull String packageName,
    @NonNull Clock clock,
    @NonNull PermissionController permissionController
)

Public methods

@NonNull AggregationResult
@NonNull List<@NonNull AggregationResultGroupedByDuration>
@NonNull List<@NonNull AggregationResultGroupedByPeriod>
void
deleteRecords(
    @NonNull KClass<@NonNull Record> recordType,
    @NonNull TimeRangeFilter timeRangeFilter
)

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

void
deleteRecords(
    @NonNull KClass<@NonNull Record> recordType,
    @NonNull List<@NonNull String> recordIdsList,
    @NonNull List<@NonNull String> clientRecordIdsList
)

Deletes one or more Record by their identifiers.

final void

Set a particular token as expired.

@NonNull ChangesResponse
getChanges(@NonNull String changesToken)

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

@NonNull String

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

@NonNull HealthConnectFeatures

Access operations related to feature availability.

final @NonNull FakeHealthConnectClientOverrides

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

final int

Overrides the page size to test pagination when calling getChanges.

@NonNull PermissionController

grants and revokes permissions.

@NonNull InsertRecordsResponse

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

@NonNull ReadRecordResponse<@NonNull T>
<T extends Record> readRecord(
    @NonNull KClass<@NonNull T> recordType,
    @NonNull String recordId
)

Reads one Record point with its recordType and recordId.

@NonNull ReadRecordsResponse<@NonNull T>
<T extends Record> readRecords(@NonNull ReadRecordsRequest<@NonNull T> request)

Returns records that match the attributes in a ReadRecordsRequest.

final void
setPageSizeGetChanges(int pageSizeGetChanges)

Overrides the page size to test pagination when calling getChanges.

void

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

Constants

DEFAULT_PACKAGE_NAME

public static final @NonNull String DEFAULT_PACKAGE_NAME

Default package name used in FakeHealthConnectClient.

Public constructors

FakeHealthConnectClient

Added in 1.0.0-alpha01
public FakeHealthConnectClient(
    @NonNull String packageName,
    @NonNull Clock clock,
    @NonNull PermissionController permissionController
)
Parameters
@NonNull String packageName

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

@NonNull Clock clock

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

@NonNull PermissionController permissionController

grants and revokes permissions.

Public methods

aggregate

public @NonNull AggregationResult aggregate(@NonNull AggregateRequest request)
Throws
kotlin.IllegalStateException

if no overrides are configured.

aggregateGroupByDuration

public @NonNull List<@NonNull AggregationResultGroupedByDurationaggregateGroupByDuration(
    @NonNull AggregateGroupByDurationRequest request
)
Throws
kotlin.IllegalStateException

if no overrides are configured.

aggregateGroupByPeriod

public @NonNull List<@NonNull AggregationResultGroupedByPeriodaggregateGroupByPeriod(@NonNull AggregateGroupByPeriodRequest request)
Throws
kotlin.IllegalStateException

if no overrides are configured.

deleteRecords

Added in 1.0.0-alpha01
public void deleteRecords(
    @NonNull KClass<@NonNull Record> recordType,
    @NonNull TimeRangeFilter timeRangeFilter
)

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
@NonNull KClass<@NonNull Record> recordType

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

@NonNull 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
public void deleteRecords(
    @NonNull KClass<@NonNull Record> recordType,
    @NonNull List<@NonNull String> recordIdsList,
    @NonNull List<@NonNull String> clientRecordIdsList
)

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
@NonNull KClass<@NonNull Record> recordType

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

@NonNull List<@NonNull String> recordIdsList

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

@NonNull List<@NonNull String> clientRecordIdsList

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
public final void expireToken(@NonNull String token)

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

getChanges

public @NonNull ChangesResponse getChanges(@NonNull String changesToken)

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
@NonNull String changesToken

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

Returns
@NonNull 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

public @NonNull String getChangesToken(@NonNull ChangesTokenRequest request)

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.

getFeatures

public @NonNull HealthConnectFeatures getFeatures()

Access operations related to feature availability.

getOverrides

Added in 1.0.0-alpha01
public final @NonNull FakeHealthConnectClientOverrides getOverrides()

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)

getPageSizeGetChanges

Added in 1.0.0-alpha01
public final int getPageSizeGetChanges()

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.

getPermissionController

Added in 1.0.0-alpha01
public @NonNull PermissionController getPermissionController()

grants and revokes permissions.

insertRecords

public @NonNull InsertRecordsResponse insertRecords(@NonNull List<@NonNull Record> records)

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

public @NonNull ReadRecordResponse<@NonNull T> <T extends Record> readRecord(
    @NonNull KClass<@NonNull T> recordType,
    @NonNull String recordId
)

Reads one Record point with its recordType and recordId.

Parameters
@NonNull KClass<@NonNull T> recordType

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

@NonNull String recordId

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

Returns
@NonNull ReadRecordResponse<@NonNull 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

public @NonNull ReadRecordsResponse<@NonNull T> <T extends Record> readRecords(@NonNull ReadRecordsRequest<@NonNull T> request)

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.

setPageSizeGetChanges

Added in 1.0.0-alpha01
public final void setPageSizeGetChanges(int pageSizeGetChanges)

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.

updateRecords

public void updateRecords(@NonNull List<@NonNull Record> records)

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
@NonNull List<@NonNull Record> records

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.