Concrete Logo
Hamburger button

Testes no Android com Espresso – Parte 4

  • Blog
  • 20 de Setembro de 2016

Esse post foi originalmente publicado no Medium pessoal do autor. Confira aqui.

Mockando requisições para a API

Há algumas semanas, comecei aqui no Blog uma série sobre testes no Android com Espresso. Se você ainda não viu e pretende começar do início, clique aqui. No post anterior (veja aqui), aprendemos como mockar as intents do Android. Caso queira iniciar a partir deste post, utilize o branch ‘part_3’ do projeto.

Cenários da MainActivity

A MainActivity possui a lista com os usuários que recebemos da API. Caso ocorra um erro na requisição, a MainActivity exibirá uma tela com um erro ao invés da lista, e enquanto nenhuma resposta volta da API um loading permanece na tela. Então, a princípio, temos três cenários para testar:

  1. Quando a requisição é feita com sucesso, devemos ver a lista com os usuários;
  2. Quando houver um erro na requisição, devemos ver a tela de erro;
  3. Quando não recebemos nenhuma resposta da API, devemos ver a tela de loading.

Ótimo, levantamos os primeiros estados para testarmos nossa activity. Porém, temos um problema com o terceiro cenário.

Nos testes que fizemos até aqui, o Espresso fez várias interações com o nosso app. Mas já parou para pensar como o Espresso sabe a hora que pode interagir com o app? Afinal de contas, os apps possuem animações e, em muitos casos, a UI só pode ser utilizada quando a animação acaba. Pois é, o Espresso faz exatamente isso, espera a UI Thread da aplicação ficar ociosa – em inglês, idle. Enquanto a UI não termina as animações, o Espresso não interage com ela. Se o app não ficar idle em 60 segundos, o Espresso devolve um erro:

Sabendo disso, você já consegue imaginar que o teste do terceiro cenário não vai funcionar, pois como a tela de loading é uma animação infinita, a UI Thread nunca vai ficar idle e o Espresso nunca vai interagir com ela. Então, vamos eliminar este cenário com esta dica muito importante e que vai te poupar muita dor de cabeça:

Cuidado com animações de ‘loading’ na tela. Se elas estiverem na tela, o seu teste com certeza vai falhar.

Escrevendo os testes

Vamos criar nossos testes para atender os dois primeiros cenários. Vou deixar a configuração inicial da classe MainActivityTest por sua conta; ela é idêntica à configuração inicial da LoginActivityTest, a única diferença é no último parâmetro para criar a ActivityTestRule. Na LoginActivityTest usamos true, nesta vamos usar false. Lembra para que serve este parâmetro? Ele indica se a activity deve ser iniciada automaticamente.

Ao final desta configuração, sua classe deve estar desta maneira:

Ok, poderíamos simplesmente sair escrevendo os testes da mesma maneira que fizemos na LoginActivityTest. Porém, como esta activity faz uma requisição para a API logo que é iniciada, dependemos do resultado da requisição para que o teste dê certo ou não. Isso é ruim, pois como eu havia dito devemos manter nossos testes isolados. Vamos, então, isolar nosso teste mockando o resultado da requisição que o app faz para a API. Para isso, vamos utilizar a lib MockWebServer.

Configurando MockWebServer

Adicione a seguinte dependência no seu arquivo build.gradle:

Sincronize o projeto e vamos configurar o MockWebServer na nossa MainActivityTest. Dê uma olhada na implementação abaixo:

Declaramos dois novos métodos: setUp e tearDown. Anotamos estes métodos com Before e After, respectivamente. Os métodos anotados com Before serão executados antes da activity ser iniciada. Os métodos anotados com After serão executados ao final de cada teste. Fora isso, nada de muito especial, estamos apenas criando uma nova instância de MockWebServer e dando um start no server.

Porém, nosso app ainda está apontando para a url real da API, ou seja:

Isso significa que, se escrevermos um teste, nosso app ainda vai fazer a requisição para a API real, e não para nosso MockWebServer. Porém, pela nossa implementação, a URL é definida como uma variável no nosso arquivo build.gradle, o que torna essa redefinição de URL um pouco mais difícil. Para facilitar nossa vida, vamos usar a lib Mirror.

Importante: antes de continuar, revise como o app está fazendo as requisições para a API. Isso vai te ajudar a entender o que vamos fazer daqui pra frente.

Adicione esta dependência no seu arquivo build.gradle:

Agora vamos criar os métodos para alterar a URL da nossa classe UsersApi. Vou mostrar o código e explicar linha a linha.

Na linha 13, chamamos o método setupServerUrl(). Vamos analisá-lo:

  • Linha 22: definimos a URL do nosso MockWebServer para “/”, pegamos o valor total da URL e armazenamos na string url;
  • Linha 24 e 25: apenas definimos o nível de log do nosso objeto HttpLogginInterceptor;
  • Linha 27: criamos um objeto OkHttpClient e passamos o nosso interceptor;
  • Linha 29: referenciamos a instância de UsersApi;
  • Linha 31: criamos um novo objeto Api com o Retrofit, só que desta vez passando a URL do nosso MockWebServer;
  • Linha 38: chamamos o método setField, que recebe como parâmetros:
    1- O target, que é o objeto que terá o field alterado usando reflection com o mirror;
    2- O nome do field que será alterado;
    3- O novo valor que será atribuído ao field alterado.

É no método setField que usaremos a lib Mirror de fato. Acredito que seja bem simples entender o que ele faz, basicamente estamos falando para o Mirror pegar o field “api” do nosso target (que é um objeto da classe UsersApi) e alterar o valor deste field para o objeto que passamos no parâmetro value. Deste modo, estamos alterando a URL base do nosso projeto para apontar para o nosso MockWebServer.

É importante ressaltar que, se sua implementação te permitir definir a URL do endpoint de maneira mais simples, é bem provável que você não precise usar reflection.

Se você ficou com alguma dúvida, retome os passos anteriores antes de prosseguir; se, mesmo assim, estiver com dúvida, deixe nos comentários para que eu possa ajudar.

Agora nosso teste está isolando nosso app das chamadas da API. Porém, ainda não definimos a resposta que nosso MockWebServer vai retornar para cada caso de teste.

Mockando o retorno da request

Para podermos mockar o retorno, devemos ter um mock do objeto json igual ao que a API retorna. Isso é simples, basta simularmos uma requisição para a API no nosso navegador, por exemplo:

Então, é só você copiar o json que aparecer no navegador e colocar em uma string em um lugar acessível aos testes. Eu costumo colocar em uma interface, conforme fiz no arquivo Mocks.java.

Agora vamos escrever nosso primeiro teste para a MainActivity. Neste teste vamos verificar se, quando a API retorna com sucesso os usuários, nós visualizamos a lista de usuários na tela.

Linha 3: estamos chamando o método enqueue do MockWebServer. Este método vai enfileirar (enqueue, em inglês) o objeto MockResponse que passamos como parâmetro. Estamos definindo o responseCode como 200, ou seja, uma requisição com sucesso. Também estamos definindo o body com o mock que acabamos de copiar e colocamos na interface Mocks.java. Resumindo: estamos dizendo para o MockWebServer: “Quando chegar uma requisição para você, retorne este MockResponse” ;

Linha 4: iniciamos a activity, passamos uma intent simples, uma vez que não é necessário nenhum extra nesta activity;

Linha 5: estamos verificando se nossa lista está visível.

Rode o teste, ele deve passar. Agora veja como ficou o log:

Veja que agora nosso endpoint é o localhost, ou seja, o MockWebServer.

Conseguimos testar um cenário da nossa activity de maneira isolada. Mas ainda falta outro cenário, aquele que quando uma requisição falha (código entre 400 e 500), aparece a tela de erro. Vou deixar esse teste para você implementar. Ele é bem simples, você apenas tem que mudar o código de retorno e o body.

Se algo deu errado, retome os passos anteriores ou deixe um comentário abaixo para eu poder te ajudar. Ao final desta etapa (após ter implementado o segundo cenário de teste) seu código deve estar parecido com o da branch ‘part_4’.

Se tiver alguma dúvida, sugestão, ou encontrou um erro no post, deixe seu comentário abaixo.

Ainda precisamos testar se o layout que definimos para o nosso item da recycler view está sendo apresentado corretamente. Também precisamos verificar se ao clicar em um item, enviamos as informações corretas para a activity de detalhes. E estes serão nossos próximos cenários de teste, nos próximos posts dessa série. Até lá!

É desenvolvedor Android e quer trabalhar com a gente? Acesse aqui!