Usar cópias de teste no Android

Quando você testa um elemento ou sistema de elementos, faz isso em isolamento. Por exemplo, para testar um ViewModel, você não precisa iniciar um emulador e lançar uma interface porque ele não depende (ou não deveria depender) do framework do Android.

No entanto, o sujeito em teste pode depender de outros para funcionar. Por exemplo, um ViewModel pode depender de um repositório de dados para funcionar.

Quando você precisa fornecer uma dependência para um objeto em teste, uma prática comum é criar um teste duplo (ou objeto de teste). As cópias de teste são objetos que aparentam e atuam como componentes no app, mas são criadas no teste para fornecer um comportamento ou dados específicos. A principal vantagem é que eles tornam os testes mais rápidos e simples.

Tipos de duplas de teste

Há vários tipos de duplas de teste:

Falso Um teste duplo que tem uma implementação "funcional" da classe, mas é implementado de forma a ser bom para testes, mas inadequado para produção.

Exemplo: um banco de dados na memória.

As falsificações não precisam de uma estrutura fictícia e são leves. Eles são preferencial.

Modelo Um duplo de teste que se comporta da mesma forma que você o programa para se comportar e que tem expectativas sobre suas interações. Os modelos vão falhar nos testes se as interações não corresponderem aos requisitos definidos. Geralmente, as simulações são criadas com um framework de simulação para fazer tudo isso.

Exemplo: verifique se um método em um banco de dados foi chamado exatamente uma vez.

Stub Um duplo de teste que se comporta como você programou, mas não tem expectativas sobre as interações. Geralmente criado com um framework de simulação. As falsificações são preferidas em vez de stubs para simplificar.
Fictício Um teste duplo que é transmitido, mas não usado, por exemplo, se você só precisar fornecê-lo como um parâmetro.

Exemplo: uma função vazia transmitida como um callback de clique.

Espião Um wrapper sobre um objeto real que também rastreia algumas informações adicionais, semelhante a mocks. Elas geralmente são evitadas porque adicionam complexidade. Portanto, é melhor usar falsificações ou simulações em vez de espiões.
Sombra Fake usado no Robolectric.

Exemplo de uso de simulação

Suponha que você queira fazer um teste unitário de um ViewModel que depende de uma interface chamada UserRepository e expõe o nome do primeiro usuário para uma interface. É possível criar um double de teste falso implementando a interface e retornando dados conhecidos.

object FakeUserRepository : UserRepository {
    fun getUsers() = listOf(UserAlice, UserBob)
}

val const UserAlice = User("Alice")
val const UserBob = User("Bob")

Esse UserRepository fictício não precisa depender das fontes de dados locais e remotas que a versão de produção usaria. O arquivo está no conjunto de origem de teste e não é enviado com o app de produção.

Uma dependência falsa pode retornar dados conhecidos sem depender de fontes de dados remotas.
Figura 1: uma dependência falsa em um teste de unidade.

O teste a seguir verifica se o ViewModel expõe corretamente o nome do primeiro usuário à visualização.

@Test
fun viewModelA_loadsUsers_showsFirstUser() {
    // Given a VM using fake data
    val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init

    // Verify that the exposed data is correct
    assertEquals(viewModel.firstUserName, UserAlice.name)
}

Substituir o UserRepository por um falso é fácil em um teste de unidade, porque o ViewModel é criado pelo testador. No entanto, pode ser difícil substituir elementos arbitrários em testes maiores.

Substituição de componentes e injeção de dependência

Quando os testes não têm controle sobre a criação dos sistemas em teste, substituir componentes por duplos de teste se torna mais complexo e exige que a arquitetura do app siga um design testável.

Até mesmo testes de ponta a ponta grandes podem se beneficiar do uso de duplos de teste, como um teste de interface instrumentado que navega por um fluxo de usuário completo no app. Nesse caso, talvez seja melhor tornar o teste hermético. Um teste hermético evita todas as dependências externas, como a busca de dados da Internet. Isso melhora a confiabilidade e o desempenho.

Figura 2: um grande teste que cobre a maior parte do app e falsifica dados remotos.

Você pode projetar seu app para ter essa flexibilidade manualmente, mas recomendamos o uso de um framework de injeção de dependência, como o Hilt, para substituir componentes do app no momento do teste. Consulte o Guia de teste do Hilt.

Próximas etapas

A página Estratégias de teste mostra como melhorar sua produtividade usando diferentes tipos de testes.