É possível avaliar a lógica do app por meio de testes de unidade locais quando você precisa executar testes com mais rapidez e não requer a fidelidade e a confiança associadas à execução de testes em um dispositivo real. Com essa abordagem, você normalmente atende aos relacionamentos das dependências usando o Robolectric ou um framework de simulação, como o Mockito (em inglês). Em geral, os tipos de dependências associadas aos seus testes determinam que ferramenta usar:
- Se você tem dependências no framework do Android, especialmente aquelas que criam interações complexas com o framework, é melhor incluir dependências do framework por meio do Robolectric.
- Se seus testes têm dependências mínimas no framework do Android ou se eles dependem apenas dos seus objetos, é possível incluir dependências simuladas por meio de um framework de simulação como o Mockito.
Configurar o ambiente de teste
No projeto do Android Studio, é preciso armazenar os arquivos de origem para testes de unidade locais em module-name/src/test/java/
. Esse diretório já existe quando você cria um novo projeto.
Também é necessário configurar as dependências de teste do projeto para usar as APIs padrão oferecidas pelo framework do JUnit 4. Se seu teste precisa interagir com dependências do Android, inclua a biblioteca do Robolectric ou do Mockito para simplificar seus testes de unidade locais.
No arquivo build.gradle
de nível superior do app, especifique as seguintes bibliotecas como dependências:
dependencies { // Required -- JUnit 4 framework testImplementation 'junit:junit:4.12' // Optional -- Robolectric environment testImplementation 'androidx.test:core:1.0.0' // Optional -- Mockito framework testImplementation 'org.mockito:mockito-core:1.10.19' }
Criar uma classe de teste de unidade local
Sua classe de teste de unidade local precisa ser programada como uma classe de teste do JUnit 4. O JUnit (em inglês) é o framework de testes de unidade mais conhecido e amplamente usado para Java. O JUnit 4 permite programar testes de uma maneira mais limpa e flexível do que as versões anteriores, uma vez que ele não exige estas ações:
- Ampliar a classe
junit.framework.TestCase
. - Adicionar prefixo ao nome do método de teste com a palavra-chave
'test'
. - Usar classes dos pacotes
junit.framework
oujunit.extensions
.
Para criar uma classe de teste básica do JUnit 4, crie uma classe que contenha um ou mais métodos de teste.
Um método de teste começa com a anotação @Test
e contém o código para exercitar e verificar uma única funcionalidade no componente que você quer testar.
O exemplo a seguir mostra como implementar uma classe de teste de unidade local. O método de teste emailValidator_CorrectEmailSimple_ReturnsTrue
verifica se o método isValidEmail()
no app em teste retorna o resultado correto.
Kotlin
import com.google.common.truth.Truth.assertThat import org.junit.Test class EmailValidatorTest { @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() { assertThat(EmailValidator.isValidEmail("name@email.com")).isTrue() } }
Java
import com.google.common.truth.Truth.assertThat; import org.junit.Test; public class EmailValidatorTest { @Test public void emailValidator_CorrectEmailSimple_ReturnsTrue() { assertThat(EmailValidator.isValidEmail("name@email.com")).isTrue(); } }
Para criar testes legíveis que avaliem se os componentes do app retornam os resultados esperados, recomendamos o uso da biblioteca Truth e das classes das declarações do Android, conforme mostrado no exemplo anterior. Para saber mais sobre os tipos de validação de lógica compatíveis com a biblioteca Truth e com as declarações do Android, consulte a seção que descreve como criar declarações mais legíveis.
No entanto, se você prefere comparar os resultados esperados e os reais usando métodos
junit.Assert ou os matchers de Hamcrest, como os métodos is()
e equalTo()
, não há problema em usar essas bibliotecas.
Observação: Hamcrest ainda é a biblioteca preferencial para a criação de matchers, por exemplo, na classe
ViewMatcher
do Espresso.
Incluir dependências do framework
Se seus testes interagem com várias dependências do framework do Android ou se eles interagem com essas dependências de maneira complexa, use os artefatos do Robolectric disponibilizados pelo AndroidX Test. O Robolectric executa código real do framework do Android e falsificações de código nativo do framework na JVM local ou em um dispositivo real.
O exemplo a seguir mostra como criar um teste de unidade que usa o Robolectric:
app/build.gradle
android { // ... testOptions { unitTests.includeAndroidResources = true } }
MyLocalUnitTestClass
Kotlin
import android.content.Context import androidx.test.core.app.ApplicationProvider import com.google.common.truth.Truth.assertThat import org.junit.Test private const val FAKE_STRING = "HELLO_WORLD" class UnitTestSample { val context = ApplicationProvider.getApplicationContext<Context>() @Test fun readStringFromContext_LocalizedString() { // Given a Context object retrieved from Robolectric... val myObjectUnderTest = ClassUnderTest(context) // ...when the string is returned from the object under test... val result: String = myObjectUnderTest.getHelloWorldString() // ...then the result should be the expected one. assertThat(result).isEqualTo(FAKE_STRING) } }
Java
import android.content.Context; import androidx.test.core.app.ApplicationProvider; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; public class UnitTestSampleJava { private static final String FAKE_STRING = "HELLO_WORLD"; private Context context = ApplicationProvider.getApplicationContext(); @Test public void readStringFromContext_LocalizedString() { // Given a Context object retrieved from Robolectric... ClassUnderTest myObjectUnderTest = new ClassUnderTest(context); // ...when the string is returned from the object under test... String result = myObjectUnderTest.getHelloWorldString(); // ...then the result should be the expected one. assertThat(result).isEqualTo(FAKE_STRING); } }
Incluir classes do Builder Android
Se você está criando testes de unidade locais executados em um ambiente do Robolectric ou em um dispositivo real, pode usar os builders que o AndroidX Test oferece para várias classes de framework comuns. Esses builders permitem criar instâncias das classes a seguir sem usar simulações ou reflexão:
Usar a classe de utilitário Parcelables
A biblioteca também oferece uma classe de utilitário para objetos Parcelable
. Oferecendo um objeto Creator
, essa classe realiza unmarshaling no objeto Parcelable
especificado e realiza marshaling em um objeto Parcelable
duplicado.
Observação: cabe ao método que chama Parcelables.forceParcel()
verificar se a operação de unmarshaling/marshaling foi bem-sucedida.
Incluir dependências simuladas
Por padrão, o Plug-in do Android para Gradle executa seus testes de unidade locais em uma versão modificada da biblioteca android.jar
, que não contém nenhum código real. Em vez disso, as chamadas de método para classes do Android feitas no teste de unidade geram uma exceção. Isso garante que você teste apenas seu código e não dependa de nenhum comportamento específico da plataforma Android (que você não tenha criado ou simulado explicitamente).
Simular dependências do Android
Se você tem dependências mínimas do Android e precisa testar interações específicas entre um componente e a dependência dele no app, use um framework de simulação para criar stubs de dependências externas no código. Dessa forma, você terá facilidade para testar se o componente interage com a dependência da maneira esperada. Substituindo dependências do Android por objetos simulados, você pode isolar seu teste de unidade do restante do sistema Android enquanto verifica se métodos corretos são chamados nessas dependências. O framework de simulação Mockito para Java (versão 1.9.5 e posteriores) oferece compatibilidade com o teste de unidade do Android. Com o Mockito, você pode configurar objetos simulados para retornar um valor específico quando chamados.
Para adicionar um objeto simulado ao teste de unidade local usando esse framework, siga este modelo de programação:
-
Inclua a dependência da biblioteca Mockito no arquivo
build.gradle
, conforme descrito em Configurar o ambiente de teste. - No início da definição da classe de teste de unidade, adicione a anotação
@RunWith(MockitoJUnitRunner.class)
. Essa anotação pede que o executor de testes do Mockito verifique se o uso do framework está correto e simplifica a inicialização dos objetos simulados. - Para criar um objeto simulado para uma dependência do Android, adicione a anotação
@Mock
antes da declaração do campo. - Para criar stubs do comportamento da dependência, especifique uma condição e retorne um valor quando a condição for atendida por meio dos métodos
when()
ethenReturn()
.
O exemplo a seguir mostra como criar um teste de unidade que usa um objeto Context
simulado.
Kotlin
import android.content.Context import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner private const val FAKE_STRING = "HELLO WORLD" @RunWith(MockitoJUnitRunner::class) class UnitTestSample { @Mock private lateinit var mockContext: Context @Test fun readStringFromContext_LocalizedString() { // Given a mocked Context injected into the object under test... `when`(mockContext.getString(R.string.hello_word)) .thenReturn(FAKE_STRING) val myObjectUnderTest = ClassUnderTest(mockContext) // ...when the string is returned from the object under test... val result: String = myObjectUnderTest.getHelloWorldString() // ...then the result should be the expected one. assertThat(result, `is`(FAKE_STRING)) } }
Java
import android.content.Context; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class UnitTestSample { private static final String FAKE_STRING = "HELLO WORLD"; @Mock Context mockContext; @Test public void readStringFromContext_LocalizedString() { // Given a mocked Context injected into the object under test... when(mockContext.getString(R.string.hello_world)) .thenReturn(FAKE_STRING); ClassUnderTest myObjectUnderTest = new ClassUnderTest(mockContext); // ...when the string is returned from the object under test... String result = myObjectUnderTest.getHelloWorldString(); // ...then the result should be the expected one. assertThat(result, is(FAKE_STRING)); } }
Para saber mais sobre o uso do framework Mockito, consulte a Referência de API Mockito e a classe SharedPreferencesHelperTest
no código de exemplo (ambos em inglês). Veja também o Codelab de testes do Android (em inglês).
Erro: "Method ... not mocked"
Se você executar um teste que chame uma API no SDK Android que você não simulou, receberá um erro informando que esse método não é simulado. Isso ocorre porque o arquivo android.jar
usado para executar testes de unidade não contém nenhum código real. Essas APIs são disponibilizadas apenas pela imagem do sistema Android em um dispositivo.
Em vez disso, todos os métodos geram exceções por padrão. Isso garante que os testes de unidade testem apenas seu código e não dependam de nenhum comportamento específico da plataforma Android (que você não tenha simulado explicitamente, por exemplo, no Mockito).
Se as exceções geradas causarem problemas para os testes, você poderá mudar o comportamento para que os métodos retornem nulo ou zero adicionando a seguinte configuração no arquivo build.gradle
de nível superior do projeto:
android { ... testOptions { unitTests.returnDefaultValues = true } }
Cuidado: a definição da propriedade returnDefaultValues
como true
precisa ser feita com atenção. Os valores de retorno nulo/zero podem introduzir regressões nos testes, que são difíceis de depurar e podem permitir que testes falhos sejam aprovados. Use-a apenas como último recurso.
Executar testes de unidade locais
Para executar testes de unidade locais, siga estas etapas:
- Verifique se o projeto está sincronizado com o Gradle clicando em Sync Project
, na barra de ferramentas.
- Execute o teste de uma das seguintes formas:
- Para executar um único teste, abra a janela Project , clique com o botão direito do mouse em um teste e clique em Run
.
- Para testar todos os métodos de uma classe, clique com o botão direito do mouse em uma classe ou método no arquivo de teste e clique em Run
.
- Para executar todos os testes de um diretório, clique com o botão direito do mouse no diretório e selecione Run tests
.
- Para executar um único teste, abra a janela Project , clique com o botão direito do mouse em um teste e clique em Run
O Plug-in do Android para Gradle compila o código do teste de unidade local armazenado no diretório padrão (src/test/java/
), cria um app de teste e o executa localmente por meio da classe do executor de testes padrão. O Android Studio exibe os resultados na janela Run.