Testes em Android – Parte I: Por onde começar?

Fala, Comunidade! Este post é o primeiro de uma série sobre testes em Android. Você aprenderá conceitos essenciais sobre a arquitetura de testes, instrumentação e como configurar o seu projeto no Android Studio para começar a automatizar seus testes unitários e de UI. Nesse primeiro post, irei focar apenas em testes unitários utilizando a ferramenta JUnit. Como ele é de assunto intermediário, caso você seja iniciante em Android, sugiro este post onde falo passo a passo como começar o desenvolvimento de apps no Android Studio.

Por que devo testar?

Nos dias de hoje, onde temos vários serviços e plataformas de Integração Contínua (Continuous Integration) gerando builds a todo vapor como Jenkins, Travis CI, Circle CI, BitBucket Pipelines, testar é algo essencial e obrigatório para que consigamos montar uma cultura de desenvolvimento ágil e de Entrega Contínua (Continuous Delivery) de produtos de qualidade para nossos clientes.

E num mundo em que vivemos cheio de mudanças, precisamos garantir que determinada funcionalidade se comporte como esperado. Além disso, com uma cobertura boa de testes, conseguimos garantir que atualizações no sistema não quebrem funcionalidades já existentes. Sem falar que, com testes automatizados, basicamente temos um roteiro documentado e pronto de como aquela funcionalidade deve se comportar.

Mas, o que devo testar?

Essa pergunta é muito difícil de responder, pois depende das características do projeto, como escopo, arquitetura e tipos de testes (unitário, de estresse, funcional, etc). Mas como regra geral, devemos testar:

  • Se determinada feature se comporta da maneira esperada
  • Cenários possíveis que o sistema pode assumir
  • Se determinada regra de negócio está correta
  • Se determinada dependência satisfaz a classe

Testes podem denunciar que sua arquitetura precisa ser refatorada. Por exemplo, se o seu projeto está muito difícil de testar de forma isolada, provavelmente a sua arquitetura está muito acoplada, quebrando os princípios SOLID. Existem várias abordagens de testes como o TDD (Test-Driven Development), BDD (Behavior-Driven Development), ATDD (Acceptance Test-Driven Development) que auxiliam para a construção e/ou evolução arquiteturas resilientes.

Estrutura de testes no Android Studio

Quando um projeto é criado no Android Studio, basicamente cada módulo contém duas pastas essenciais para os testes de acordo com o escopo do teste: teste de UI ou teste unitário.

  • src/androidTest: Esta pasta contém todos os testes de UI, conhecidos também como Testes de Instrumentação (Instrumentation Tests). Esses tipos de testes comumente simulam a interação do usuário com as várias partes do sistema, como clique em botão, arraste, rolagem na tela (scroll); ou asserção de elementos na estrutura do layout, como checagem de visibilidade, se o componente pertence à hierarquia, se está corretamente alinhado e muito mais; ou ainda se determinada ação (Intent) deve ser executada. Não se preocupe, iremos explorar esses testes bastante quando estivermos falando de Espresso.
  • src/test: Esta pasta contém os testes unitários para classes de modelo, de comunicação com API REST, persistência, etc. Muitas vezes, para simular o comportamento de dependências de uma classe utilizamos o conceito de mock objects. Não se preocupe, iremos ver isso com mais detalhe no próximo post.

 Configurando as dependências no Gradle

Ao criar um projeto no Android Studio, a dependência do JUnit já é automaticamente adicionada no arquivo build.gradle do módulo, junto com outras também. Caso não esteja, adicione ela em dependencies, como mostrado no trecho abaixo. O comando testCompile indica ao Gradle para exigir a dependência apenas no escopo de teste. Boa prática: adicione apenas as dependências de acordo com o seu escopo, pois se você utilizar o comando genérico compile, a dependência será empacotada no APK e que irá influenciar drasticamente no tamanho final.

[code language=\\\”Groovy\\\”]

dependencies {
// outras dependências de libs
testCompile \\\’junit:junit:4.12\\\’
}

[/code]

Relembrando conceitos: Testes Unitários

Como o próprio nome sugere, um teste unitário tem o propósito de testar isoladamente o comportamento de uma unidade, que pode ser à nível de classe ou métodos. Em alguns casos, quando a estrutura da classe a ser testada é mais complexa e depende de outras classes, podemos imitar essas dependências por meio de mock objects (a palavra mock em inglês significa imitar). 

Boas práticas: convenções de nomes de classes e métodos

Em um teste unitário (também comumente conhecido como teste de \\\”caixa-branca\\\”), recomendamos seguir algumas boas práticas e convenções para nomes de classes e métodos para maior legibilidade. Suponha que você precise testar e garantir que determinada condição deva ser satisfeita. Você pode estruturar seus testes da seguinte maneira:

[code language=\\\”Java\\\”]
public void shouldValidateIfConditionIsSatisfied(){ … }
[/code]

Criando a classe a ser testada

Para fins didáticos, como exemplo iremos criar a classe BuildVersion que representa uma versão de build da plataforma Android, como Lollipop, Marshmallow, Nougat, etc.

[code language=\\\”Java\\\”]
public final class BuildVersion {

// getters and setters

public static boolean isNougat(){
return (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1);
}
}
[/code]

Criando a classe de teste

No JUnit 4, não é necessário estender TestCase como fazíamos com a versão anterior do framework. Para nomes de classes, é mais intuitivo nomear como NomeDaClasseParaTestarTest. Isso deixa bem claro o contexto que está sendo testado. No nosso exemplo, um bom nome da classe seria BuildVersionTest. 

[code language=\\\”Java\\\”]
public class BuildVersionTest {
// métodos de testes aqui
}
[/code]

Criando métodos para testes

O JUnit 4 possui algumas anotações padrões que devem ser utilizadas quando se necessita realizar uma pré-configuração antes (@Before) ou depois (@After) da execução de um método de teste ou antes (@BeforeClass) e depois (@AfterClass) da execução de uma classe de teste. Além disso, todo teste deve ser anotado com @Test. Agora vamos criar um teste simples: validar se a versão que o usuário está utilizando é a Nougat.

[code language=\\\”Java\\\”]
public class BuildVersionTest {
@Test
public void shouldValidateIfBuildVersionIsNougat(){
boolean isNougat = BuildVersion.isNougat();
assertTrue(isNougat);
}
}
[/code]

Executando testes no Android Studio

Para executar um teste, basta abrir a classe de teste. Clicando no play duplo ao lado esquerdo do nome da classe, você consegue executar ou depurar a classe de teste. Caso você queira executar apenas um método na classe de teste, basta você clicar no play ao lado esquerdo do método. Aguarde a execução finalizar. Para checar o status do teste, basta você verificar na aba Run ou Debug se os testes passaram (em verde) ou se falharam (em vermelho). Caso você queira gerar um relatório mais detalhado dos testes executados, basta executar o seguinte comando:

[code language=\\\”shell\\\”]
$ ./gradlew test
[/code]

Isso irá informar ao Gradle para ele executar os testes para todas as buildTypes, que por padrão é a debug e a release. Caso queira gerar o relatório para determinada buildType, basta executar o seguinte comando:

[code language=\\\”shell\\\”]
$ ./gradlew testDebug
[/code]

Depois disso, o(s) relatório(s) será(ão) gerado(s) no diretório build/reports/tests/build_type. O arquivo index.html é uma página web com os detalhes dos testes separados por pacotes e classes.

E agora?

E aí, nunca pensou que testar em Android fosse tão fácil? 🙂 Ficou empolgado? Então não perca o próximo artigo onde irei falar sobre como utilizar mocks com Mockito e PowerMock.

Até a próxima!

Links

JUnit 4 Framework

Getting Started with Testing

Best Practices for Testing

Test from the Command Line

Testes no Android

Building Effective Unit Tests


Comentários

Uma resposta para “Testes em Android – Parte I: Por onde começar?”

  1. Muito bom, Ramon!

    É sempre útil realizar testes automatizados. No desenvolvimento Android isto se torna mais crítico, pois, dependendo das configurações de hardware do computador, o emulador virtual do smartphone pode não funcionar a contento.

    Estou no aguardo da continuação.

    Abraço!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *