Criar testes de unidade locais

Um teste local é executado diretamente na sua própria estação de trabalho, e não em um dispositivo ou emulador Android. Dessa forma, ele usa sua máquina virtual Java (JVM) local, em vez de um dispositivo Android, para executar testes. Os testes locais permitem avaliar a lógica do app mais rapidamente. No entanto, não poder interagir com o framework do Android cria uma limitação nos tipos de testes que podem ser executados.

Um teste de unidade verifica o comportamento de uma pequena seção do código, a unidade em teste. Ele faz isso executando esse código e verificando o resultado.

Os testes de unidade geralmente são simples, mas a configuração deles pode ser problemática quando a unidade em teste não foi projetada com a capacidade de teste em mente:

  • O código que você quer verificar precisa ser acessível em um teste. Por exemplo, não é possível testar um método particular diretamente. Em vez disso, você testa a classe usando as APIs públicas dela.
  • Para executar testes de unidade em isolamento, as dependências da unidade em testes precisam ser substituídas por componentes que você controla, como falsificações ou outras duplicações de teste. Isso é especialmente problemático caso seu código dependa do framework do Android.

Para saber mais sobre estratégias comuns de testes de unidade no Android, leia O que testar.

Local dos testes locais

Por padrão, os arquivos de origem para testes de unidade locais são colocados em module-name/src/test/. Esse diretório já existe quando você cria um novo projeto usando o Android Studio.

Como adicionar dependências de teste

Também é necessário configurar as dependências de teste do projeto para usar as APIs padrão fornecidas pelo framework de testes JUnit (em inglês).

Para fazer isso, abra o arquivo build.gradle do módulo do app e especifique as bibliotecas abaixo como dependências. Use a função testImplementation para indicar que elas se aplicam ao conjunto de origem do teste local, e não ao aplicativo:

dependencies {
  // Required -- JUnit 4 framework
  testImplementation "junit:junit:$jUnitVersion"
  // Optional -- Robolectric environment
  testImplementation "androidx.test:core:$androidXTestVersion"
  // Optional -- Mockito framework
  testImplementation "org.mockito:mockito-core:$mockitoVersion"
  // Optional -- mockito-kotlin
  testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
  // Optional -- Mockk framework
  testImplementation "io.mockk:mockk:$mockkVersion"
}

Criar uma classe de teste de unidade local

Crie a classe de teste de unidade local como JUnit 4.

Para fazer isso, crie uma classe que contenha um ou mais métodos de teste, geralmente em module-name/src/test/. Um método de teste começa com a anotação @Test e contém o código para exercitar e verificar um único aspecto do componente que você quer testar.

O exemplo abaixo demonstra como implementar uma classe de teste de unidade local. O método de teste emailValidator_correctEmailSimple_returnsTrue() tenta verificar isValidEmail(),que é um método dentro do app. A função de teste vai retornar "true" se isValidEmail() também retornar "true".

Kotlin


import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class EmailValidatorTest {
  @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"))
  }

}

Java


import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

class EmailValidatorTest {
  @Test
  public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
    assertTrue(EmailValidator.isValidEmail("name@email.com"));
  }
}

Crie testes legíveis que avaliem se os componentes do seu app retornam os resultados esperados. Recomendamos que você use uma biblioteca de declarações, como junit.Assert, Hamcrest ou Truth. O snippet acima é um exemplo de como usar junit.Assert.

Biblioteca Android simulada

Quando você executa testes de unidade locais, o Plug-in do Android para Gradle inclui uma biblioteca que contém todas as APIs do framework do Android, correta para a versão usada no seu projeto. A biblioteca contém todos os métodos e classes públicos dessas APIs, mas o código dentro dos métodos foi removido. Se algum dos métodos for acessado, o teste vai gerar uma exceção.

Isso permite que testes locais sejam criados ao referenciar classes no framework do Android, como Context. Mais importante ainda, permite que você use um framework de simulação com classes do Android.

Simular dependências do Android

Um problema típico é descobrir que uma classe está usando um recurso de string. Você pode receber recursos de string chamando o método getString() na classe Context. No entanto, um teste local não pode usar Context ou qualquer um dos métodos relacionados, já que pertence ao framework do Android. O ideal é que a chamada para getString() seja removida da classe, mas isso nem sempre é prático. A solução é criar uma simulação ou um stub de Context que sempre retorna o mesmo valor quando o método getString() é invocado.

Com a biblioteca Mockable Android e frameworks de simulação, como Mockito ou MockK, é possível programar o comportamento de simulações das classes do Android nos seus testes de unidade.

Para adicionar um objeto simulado ao teste de unidade local usando o Mockito, siga este modelo de programação:

  1. Inclua a dependência da biblioteca Mockito no arquivo build.gradle, conforme descrito em Configurar o ambiente de teste.
  2. No início da definição da classe de teste de unidade, adicione a anotação @RunWith(MockitoJUnitRunner.class). Essa anotação instrui o executor de testes do Mockito a validar se o uso do framework está correto e simplifica a inicialização dos objetos simulados.
  3. Para criar um objeto simulado para uma dependência do Android, adicione a anotação @Mock antes da declaração do campo.
  4. Para criar stubs do comportamento da dependência, especifique uma condição e retornar um valor quando a condição for atendida usando os métodos when() e thenReturn().

O exemplo abaixo mostra como criar um teste de unidade que usa um objeto Context simulado em Kotlin criado com o Mockito-Kotlin.

import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

private const val FAKE_STRING = "HELLO WORLD"

@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {

  @Mock
  private lateinit var mockContext: Context

  @Test
  fun readStringFromContext_LocalizedString() {
    // Given a mocked Context injected into the object under test...
    val mockContext = mock<Context> {
        on { getString(R.string.name_label) } doReturn FAKE_STRING
    }

    val myObjectUnderTest = ClassUnderTest(mockContext)

    // ...when the string is returned from the object under test...
    val result: String = myObjectUnderTest.getName()

    // ...then the result should be the expected one.
    assertEquals(result, FAKE_STRING)
  }
}

Para saber mais sobre como usar o framework Mockito, consulte a Referência da API Mockito e a classe SharedPreferencesHelperTest no exemplo de código (link em inglês). Veja também o Codelab de testes do Android.

Erro: "Method ... not mocked"

A biblioteca Mockable Android vai gerar uma exceção se você tentar acessar qualquer um dos métodos com a mensagem Error: "Method ... not mocked.

Se as exceções geradas causarem problemas para os testes, você poderá mudar o comportamento para que os métodos retornem nulo ou zero, dependendo do tipo de retorno. Para fazer isso, adicione a seguinte configuração no arquivo build.gradle de nível superior do projeto no Groovy:

android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }