Criar testes de unidade usando a biblioteca de testes do app Conexão Saúde

Biblioteca de testes do app Conexão Saúde (androidx.health.connect:connect-testing) simplifica a criação de testes automatizados. Você pode usar esta biblioteca para verificar o comportamento do aplicativo e confirmar se ele responde corretamente casos incomuns, difíceis de testar manualmente.

Você pode usar a biblioteca para criar testes de unidade locais, que geralmente verificam o comportamento das classes no app que interagem com a Conexão Saúde cliente.

Para começar a usar a biblioteca, adicione-a como uma dependência de teste:

 testImplementation("androidx.health.connect:connect-testing:1.0.0-alpha01")

O ponto de entrada para a biblioteca é a classe FakeHealthConnectClient, que você usado em testes para substituir HealthConnectClient. O FakeHealthConnectClient tem os seguintes recursos:

  • Uma representação de registros na memória, para que você possa inserir, remover, excluir e ler
  • Geração de tokens de mudança e acompanhamento de mudanças
  • Paginação de registros e alterações
  • As respostas de agregação são compatíveis com stubs
  • Permite que qualquer função gere exceções
  • Um FakePermissionController que pode ser usado para emular verificações de permissões.

Para saber mais sobre como substituir dependências em testes, leia Injeção de dependências no Android. Para saber mais sobre conteúdo falso, leia Como usar testes duplos no Android.

Por exemplo, se a classe que interage com o cliente for chamada HealthConnectManager e usa um HealthConnectClient como dependência, ficaria assim:

class HealthConnectManager(
    private val healthConnectClient: HealthConnectClient,
    ...
) { }

Em testes, você pode transmitir um falso para a classe em teste:

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

Este teste verifica se a função fetchReport fictícia na O HealthConnectManager filtra corretamente os registros por atividade.

Como verificar exceções

Quase todas as chamadas para HealthConnectClient podem gerar exceções. Por exemplo: a documentação de insertRecords menciona estas exceções:

  • @throws android.os.RemoteException para qualquer falha de transporte da IPC.
  • @throws SecurityException para solicitações com acesso não permitido.
  • @throws java.io.IOException para qualquer problema de E/S de disco.

Essas exceções abrangem casos como uma conexão ruim ou falta de espaço no dispositivo. Seu app precisa reagir corretamente a esses problemas de execução, já que eles podem acontecer a qualquer momento.

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

Agregação

As chamadas de agregação não têm implementações falsas. Em vez disso, as chamadas de agregação usar stubs que podem ser programados para se comportar de uma determinada maneira. Acesse o usando a propriedade overrides do FakeHealthConnectClient.

Por exemplo, é possível programar a função agregada para retornar um resultado específico:

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)

Em seguida, verifique se a classe está em teste (HealthConnectManager) neste caso, processou o resultado corretamente:

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

Permissões

A biblioteca de testes inclui um FakePermissionController, que pode ser transmitido como uma dependência de FakeHealthConnectClient.

O objeto em testes pode usar o PermissionController—through propriedade permissionController da interface HealthConnectClient. Para verificar, para saber as permissões. Isso normalmente é feito antes de cada chamada para o cliente.

Para testar essa funcionalidade, você pode definir quais permissões estão disponíveis usando o 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)
}

Paginação

A paginação é uma fonte muito comum de bugs, então FakeHealthConnectClient fornece mecanismos para ajudar a verificar se a implementação de paginação para registros e alterações se comporta corretamente.

O objeto em teste, HealthConnectManager em nosso exemplo, pode especificar a tamanho da página no ReadRecordsRequest:

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

Definir o tamanho da página com um valor pequeno, como 2, permite testar facilmente paginação. Por exemplo, é possível inserir cinco registros para que readRecords retorne 3. páginas diferentes:

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

Dados de teste

A biblioteca ainda não inclui APIs para gerar dados falsos, mas você pode usar o dados e geradores usados pela biblioteca na Pesquisa de código do Android.

Stubs

A propriedade overrides de FakeHealthConnectClient permite programar (ou criar stubs) de qualquer uma das funções para gerar exceções quando chamadas. As chamadas de agregação também podem retornar dados arbitrários e oferece suporte ao enfileiramento múltiplas respostas. Consulte Stub e MutableStub para mais informações.

Resumo dos casos extremos

  • Verifique se o aplicativo se comporta conforme o esperado quando o cliente gera exceções. Verifique a documentação de cada função para descobrir quais exceções você o que deve ser verificado.
  • Verifique se cada chamada feita para o cliente é precedida pelo verificação de permissões.
  • Verifique sua implementação de paginação.
  • Verifique o que acontece quando você busca várias páginas, mas uma delas tem o URL expirado com base no token correto anterior.