Kotlin + Espresso — Testes Automatizados e Expressivos para Android

Jefferson Tavares de Pádua
3 min readFeb 25, 2018

--

Photo by SHTTEFAN on Unsplash

Desenvolver um aplicativo é sempre um desafio, e garantir a manutenabilidade de seu código ao longo do tempo é um desafio maior ainda. Antes de lançar uma atualização, ainda que uma pequena mudança tenha sido implementada é sempre bom ter certeza de que todas as funcionalidades disponíveis no aplicativo continuam operando de maneira correta. Mas como garantir isso?

Boa parte dos programadores se preocupam em testar apenas a lógica do nosso sistema (isso quando o fazem), e se esquecem que toda a interação do usuário com o aplicativo se dá por meio da interface. Isso quer dizer que a simples inversão do momento em que um botão está habilitado ou desabilitado pode fazer com que o usuário não consiga utilizar o aplicativo.

Logo, é extremamente importante realizar testes na interface de um app após modificar seu código fonte, e à medida que seu número de funcionalidades aumenta, torna-se inviável realizar esse processo de maneira manual.

Por essa razão, existem diversas ferramentas que possibilitam a criação de testes automatizados. A principal vantagem de se automatizar os testes de interface é a certeza de que, se todos os testes passarem, o aplicativo está funcionando corretamente, uma vez que falhas humanas como a subjetividade e a negligência são removidas do processo.

Nesse sentido, a plataforma android conta com diversas opções para o desenvolvimento de testes de interface automatizados, sendo UI Automator, Robolectric e Espresso algumas das bibliotecas disponíveis. Nesse artigo será feito uma introdução à utilização desse último.

Configuração

Antes de mais nada é necessário adicionar a biblioteca ao projeto. Para isso basta adicionar as dependências abaixo no arquivo build.gradle:

androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1'androidTestCompile 'com.android.support.test:runner:1.0.1'androidTestCompile 'com.android.support.test.espresso:espresso-intents:3.0.1'

O primeiro link se refere ao “core” da biblioteca, por isso sempre deverá ser adicionado. O segundo link também é obrigatório, pois contém “adaptadores” da biblioteca de testes para o JUnit. Já oúltimo link permite a verificação de intents sendo criadas durante o teste, portanto só deverá ser adicionada em casos onde esse tipo de verificação seja necessária.

Conceitos Básicos

De forma simplificada, o Espresso pode ser separado em 3 grandes grupos de informações: ViewMatcher, ViewAction e ViewAssertion.

ViewMatchers tem por objetivo encontrar uma determinada view dentro da hierarquia do layout atual.

onView(withId(R.id.fab))

ViewActions serão responsáveis por realizar interações com a view encontrada pelo ViewMatcher:

onView(withId(R.id.fab))
.perform(click())

ViewAssertion é uma verificação do estado em que você espera que a interface se encontre após uma ViewAction:

onView(withId(R.id.fab))
.perform(click())
.check(matches(not(isEnabled())

Por meio da combinação desses 3 conceitos é possível criar praticamente qualquer tipo de teste, independentemente de quão complexa é a sua interface.

Note que o método not na verdade é importado de android.support.test.espresso.* mas sim de org.hamcrest. Isso é possível porque o método matches recebe um Matcher. Na prática isso quer dizer que qualquer um dos inúmeros Matchers presentes na biblioteca Hamcrest podem ser utilizados nesse contexto sem nenhum problema.

Um exemplo do mundo real

Uma vez que garantir a segurança dos dados do usuário é imprescindível, boa parte dos apps atualmente contam com um sistema de autenticação. Nesse tipo de app o usuário digita seu login e senha aperta o botão confirmar e depois é redirecionado para um dashboard caso os dados estejam corretos. Vamos ver como fica um teste automatizado para esse tipo de caso?

onView(withId(R.id.edit_text_login))
.perform(typeText("jefferson@gmail.com"))

onView(withId(R.id.edit_text_senha))
.perform(typeText("123456"))

onView(withId(R.id.button_confirmar))
.perform(click())

intended(hasComponent("com.dominio.app.DashboardActivity"))

No exemplo acima o código é praticamente o mesmo dos exemplos anteriores, exceto pelos ViewActions typeText que, como o próprio nome indica, irão escrever o valor entre aspas no campo especificado.

Além disso a última linha mostra a utilização de um IntentMatcher que testa se a DashboardActivity foi iniciada após o click no botão confirmar. Simples não é?

Por fim, para garantir que esse código funciona, precisamos colocá-lo em uma classe com as configurações especificas para execução do código. A versão final ficaria assim:

@RunWith(AndroidJUnit4::class)
@LargeTest
class ExampleInstrumentedTest {
@Rule
@JvmField
val rule = IntentsTestRule(LoginActivity::class.java)

@Test
fun useAppContext() {
onView(withId(R.id.edit_text_login))
.perform(typeText("jefferson@gmail.com"))

onView(withId(R.id.edit_text_senha))
.perform(typeText("123456"))

onView(withId(R.id.button_confirmar))
.perform(click())

intended(hasComponent("com.dominio.app.DashboardActivity"))
}
}

Nesse caso a atenção deve ser dirigida para a criação da regra de execução do teste, IntentsTestRule. Caso essa regra não seja utilizada, uma exceção será lançada no momento em que é verificado se a DashboardActivity foi iniciada, portanto fique atento.

Espero que esse artigo tenha lhe ajudado a entender como utilizar o Espresso para automatização dos testes em seu app.

Até a próxima.

--

--