Alura > Cursos de Mobile > Cursos de Android > Conteúdos de Android > Primeiras aulas do curso Testes no Android: Mocks e integrações

Testes no Android: Mocks e integrações

Começando com os testes do projeto legado - Introdução

Olá, eu sou o Alex Felipe e vou ministrar para vocês o curso Testes no Android: mocks e integrações.

Nosso objetivo é analisar e testar novos comportamentos adquiridos ao nosso aplicativo de leilão em cursos anteriores de testes de automatizados, logo, o pré requisito deste curso é o conteúdo dos cursos iniciais (Android parte 1: testes automatizados e TDD e Android parte 2: boas práticas e novos cenários de testes).

Mas quais são esses novos comportamentos?

Um deles está relacionado à nossa lista de leilão que antes adicionávamos a partir do próprio código. Agora, nós recebemos esses leilões a partir de uma API que também nos provém estes dados. Teremos, portanto, uma aplicação web que simula uma API de leilões públicos.

Nessa aplicação, conseguimos cadastrar leilões no campo de preenchimento com "Descrição" e, automaticamente, os dados são consumidos, enviados e recebidos. Se abrirmos a app novamente, veremos que ele carrega os leilões que cadastramos pela web. Este é o primeiro comportamento a ser explorado no presente curso.

Além disso, temos uma nova tela, responsável pelo cadastro de usuários, que pode ser acessada ao clicarmos na silhueta de uma pessoa localizada ao canto superior direito na tela do aplicativo. Para cadastrar os usuários, bastar selecionar + e escolher o nome.

Agora que temos o leilão e seus usuários, podemos propor lances clicando na tela correspondente - criada nos cursos anteriores. Adicionamos um lance clicando no botão com + e digitando o valor no campo de preenchimento. A regra que aplicamos para impedir trapaças também está operando, logo, não é possível adicionar um lance mais baixo do que o último valor proposto. Caso alguém tente, aparecerá uma mensagem de alerta.

Nosso curso consistirá, então, em analisar e aplicar testes para esses três comportamentos. Porém, teremos diversos desafios durantes os testes pois, ao lidarmos com aqueles que envolvem Android Framework, existem alguns comportamentos que nos forçam a usar testes instrumentais. Como esse tipo de teste não será abordado com profundidade, lidaremos com outras alternativas viáveis, como objetos simulados ou objetos mocks.

A respeito da integração, percebemos a necessidade de testar a comunicação entre a app e API, o que também será testada com objetos simulados e com testes assíncronos, para entendermos o desafio deste em comparação aos benefícios daquele.

O desafio será entender este lado avançado sobre testes de unidades com integração, simulando esses comportamentos supracitados. Para nos auxiliar, utilizaremos uma biblioteca famosa no mundo Java chamada Mockito.

Por ora é isto, espero que você esteja animado(a) com o conteúdo do curso, que pode se complicar as vezes, mas farei o máximo para descomplicar para você. Até mais.

Começando com os testes do projeto legado - Preparando o ambiente

Para começar, devemos abrir o Android Studio - é recomendável que você use a mesma versão, 3.1.3, para que haja paridade gráfica entre os projetos. Agora, precisamos acessar o projeto criado nos cursos anteriores, e que foi disponibilizado para você anteriormente. O arquivo está em .zip e contém o projeto Android, bem como o servidor que utilizaremos.

Pressionando a tecla direita do mouse e selecionando a opção "Extrair tudo", será indicado para onde desejamos extrair - recomendo que escolha o arquivo onde está o .zip, o \projeto\. Abrindo o arquivo extraído, encontraremos os diretórios "Leilao" e "servidor", nossa app e servidor, respectivamente.

Uma forma de abrir o projeto no Android Studio é selecionando "Open an existing Android Studio project" e escrever o nome do arquivo na área de busca. Outra forma é deixar a janela do arquivo aberta e arrastar a pasta até o programa. Vamos executar o projeto para verificar se há algum problema; para isso damos o play com a seta verde da barra de ferramentas e o programa abrirá a janela "Select Deployment Target" e solicitará o dispositivo - neste caso, Pixel API 27, porém é possível utilizar qualquer API que preferirmos.

Inicialmente o Studio apresentará a mensagem de que não foi possível carregar os leilões. Isso ocorre porque eles não são criados por nós, mas carregados a partir da API (nosso servidor). Sendo assim, voltemos à pasta "servidor", em que há um arquivo server.jar, possível de ser executado com dois cliques. Entretanto, desta maneira, será exigido um Windows Task Manager para que façamos o fim da execução manualmente. É recomendável, portanto, que se execute o servidor por linha de comando no prompt do Windows ou terminal de comando do Linux ou Mac OS.

Primeiro vamos abrir o cmd, prompt de comando do Windows, na tela inicial. Devemos colocar cd para abrir um diretório e depois colar o endereço do nosso servidor, que pode ser copiado ao selecionarmos a palavra "servidor" e, com o botão direito do mouse, clicarmos em "Copiar endereço".

Se dermos "Enter" desta maneira, não acontecerá nada, porque necessitamos antes acessar o volume D:, onde se localiza o arquivo a ser executado. Para acessarmos o server, precisamos digitar dir, que gerará:

O volume na unidade D é blackystudio

O Número de Série do Volume é 7416-9942

Pasta de D: Alex\mock\projeto\android-testes-mock-arquivos-inicias\servidor

30/07/2018 10:13

.

30/07/2018 10:13

..

30/07/2018 10:13 35.966.417 server.jar

          1 arquivos(s)   35.966.417 bytes

         2 pasta(s)   87.12.733.696 bytes disponíveis

Para rodarmos o arquivo -jar, é preciso que adicionemos ao final desta última linha o comando java indicando que o arquivo pertence à classe Java, bem como -jar indicando o tipo de arquivo Java e o nome do arquivo, server.jar, de modo que a linha ficarájava -jar server.jar. Agora, ao executar, o prompt nos mostrará todos os logs que estão sendo executados.

Uma maneira de verificar se eles estão funcionando como esperado é checar se na última linha aparece a mensagem Started LeilaoApiApplicationKt. Notem também, que na terceira linha de cima para baixo, encontra-se a porta em que ele está rodando, 8080, portanto, se formos até o navegador e digitarmos localhost:8080, isto abrirá nossa API, em que adicionaremos os leilões. Para acessá-los, inclusive, basta digitarmos localhost:8080/leilao na barra de endereço do navegador.

Nosso servidor está funcionando da maneira esperada! E se quisermos finalizá-lo, basta pressionarmos "Ctrl + C". Quando o server é finalizado, precisamos refazer os comandos para que ele execute novamente. Ao fazermos isso, perceberemos que os dados anteriormente adicionados serão mantidos.

Mas se não há banco de dados instalado, como foi possível mantê-los?

Ao executarmos o server.jar, automaticamente surge o diretório "database", onde serão mantidos todos os arquivos. Isso foi feito desta forma para que não precisemos nos preocupar com a preparação do ambiente, e instalar diversos bancos de dados, nem termos dependências. Se abrirmos a app, no entanto, ela não carregará os leilões.

No Studio, na aba lateral esquerda, temos o projeto "Project" aberto, que contém a pasta "app", fazendo o caminho "app > java > api > retrofit > RetrofitInicializador". A constante URL_BASE possui a mensagem http://endereco_ip_da_api/, que deve ser preenchida com o IP do servidor. Com o atalho "Alt + F12" temos acesso ao terminal dentro do programa, e usando o comando ipconfig, podemos consultar este IP. Ele nos levará ao endereço IPv4 192.168.20.248, que é o endereço de nosso computador.

Copiaremos este endereço e o colaremos na nossa API juntamente com a porta 8080, de modo que nossa URL_BASE ficará http://192.168.20.148:8080/. Com o atalho "Shift + F10", o Android Studio será executado, e saberemos que a app conseguiu carregar os leilões. Pronto, resolvemos nosso problema de integração!

Para cada atualização de leilão pela API, é necessário sair e entrar na app para que ela atualize as informações.

Começando com os testes do projeto legado - Testando o adapter de leilões

Dando continuidade ao curso, vamos explorar as novas features vistas e identificar unidades potencialmente testáveis. Como os leilões não são criados por nós, mas importados da API, vamos explorar a implementação para verificar se existe a possibilidade de criarmos algum teste. Para explorá-lo, vamos a "ui > activity > ListaLeilaoActivity", em que veremos o seguinte código:

public class ListaLeilaoActivity extends AppCompatActivity { 

    private static final String TÍTULO APPEAR = "Leilões";
    private static final String MENSAGEM_AVISO_FALHA_AO_CARREGAR_LEILOES = "Não foi possível carregar os leilões"; 
    private final LeilaoWebClient client = new LeilaoWebClient(); 
    private ListaLeilaoAdapter adapter;
}

A segunda linha do código é referente à mensagem "Não foi possível carregar os leilões", anteriormente exibida. Na terceira linha, encontra-se o atributo client representando um leilão web client, que é uma classe que abrange toda regra de negócios para carregar os leilões, seguida pelo adapter que vimos anteriormente. Foquemos, finalmente, no fluxo da nossa aplicação:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_lista_leilao);
       getSupportActionBar().setTitle(TITULO_APPBAR);
       configuraListaLeiloes();
}

private void configuraListaLeiloes() {

    configuraAdapter();
    configuraRecyclerView();
}

private void configuraRecyclerView() {
    RecyclerView recyclerView = findViewById(R.id.lista_leilao_recyclerview); 
    recyclerView.setAdapter(adapter);
}

private void configuraAdapter() {
    adapter = new ListaLeilaoAdapter(context: this);
    adapter.setOnItemClickListener((leilao) -> {
             vaiParaTelaDeLances(leilao);
    });
}

É na configuração de onCreate() que setamos o título dos nosso leilões. Configuramos o Adapter e o RecyclerView. Em adapter, basta que criemos uma instância e setemos um Listener para que cada vez que um leilão for clicado, ele abra a tela de lances correspondente. Tão simples quanto, é o recyclerView, pois basta setar o Adapter.

@Override
protected void onResume() { 
    super.onResume(); 
    client.todos(new RespostaListener<List<Leilao>>() {
        @Override 
        public void sucesso(List<Leilao> leiloes) { adapter.atualiza(leiloes); } 

        @Overide
        public void falha(String mensagem) { 
            Toast.makeText(context: ListaLeilaoActivity.this,
                MENSAGEM_AVISO_FALHA_AO_CARREGAR_LEILOES,
                Toast.LENGTH_SHORT).show();
        }
    });
}

Em onResume(), começa a ser usado nosso client, com o método chamado de todos, que tem a responsabilidade de pegar todos os leilões. Isto é feito implementando-se a interface RespostaListener, que possui dois métodos, sucesso e falha. Atentem-se que sucesso está recebendo uma lista de leilões (List<Leilao>).

Percebam que o RespostaListener lida com uma lista de generics, e é por isso que se recebe uma lista de leilões:

public interface RespostaListener<T> {
    void sucesso(T resposta);

    void falha(String mensagem);
}

Então, todos implementa esta interface e nos devolve a lista de leilões quando ela estiver carregada a partir do retrofit. Notem que uma unidade que se repete e que ainda não está garantida no software é o adapter. Precisamos garantir que este elemento atualizará a lista de leilões independentemente se ela vier da API ou de outro comportamento. Faremos, portanto, um teste com ele para verificar seu funcionamento.

Para testá-lo, vamos primeiro a ListaLeilaoAdapter, que acessamos clicando nele e usando o atalho "Ctrl + B". Com ListaLeilaoAdapter selecionado, daremos "Ctrl + Shift + T" para abrirmos o Creator Test, onde devemos escolher o diretório de test, segundo da lista.

Selecionando-se "OK", o teste é criado. Em seguida, criaremos também o comportamento a ser testado, que é atualizar a lista de leilões quando esta for recebida, ou seja, deve_AtualizarListaDeLeiloes_QuandoReceberListaDeLeiloes().

O objeto será adapter, que precisa de uma instância, então teremos ListaLeilaoAdapter() - notem que existe uma dependência de contexto. Uma vez que o próprio framework do Android cria o contexto, assim, é melhor mandarmos uma referência nula - null - e observar o quanto isso impactará no teste.

Agora, nos falta apenas programar o comportamento esperado, por isso adapter precisa atualizar a lista. Para tanto, faremos a instância de um ArrayList com base em Arrays.asList(). Em seguida, enviaremos a instância de Leilao(), ou seja, de objetos a serem leiloados, neste caso "Computador" e "Console". A seguir, pegaremos esse valor para verificarmos a quantidade de leilões que teremos.

Começamos com Leilao generics inicial porque não era garantido que existia um valor de leilão dentro do Arrays.asList(), mas agora podemos apagá-lo, já que a conversão será feita automaticamente.

Precisamos de um valor que identifique se os leilões foram recebidos. Podemos usar a quantidade do adapter, ou seja, Adapter.getItemCount(), e devolvemos para um valor que conseguiremos a partir de uma comparação. Seu nome será quantidadeLeiloesDevolvida, a partir dela executaremos o nosso teste.

Para verificar se foram enviados dois itens, teremos dois leilões no adapter, então, para finalizar, devemos digitar assert.That(quantidadeLeilõesDevolvida, is(value: 2)). Com isso, temos nosso teste:

@Test
public void deve_AtualizarListaDeLeiloes_QuandoReceberListaDeLeiloes() { 
    ListaLeilaoAdapter adapter = new ListaLeilaoAdapter(context: null);

    adapter.atualiza(new_ArrayList<>(Arrays.asList(
        new Leilao(descricao:"Console"),'
        new Leilao(descricao:"Computador")

    )));
    int quantodadeLeiloesDevolvida = adapter.getItemCount();

    assertThat(quantidadeLeiloesDevolvida, is(value:2));
}

Executando com "Ctrl + Shift + F10", o teste apresenta uma falha Null Point Exception na linha 56, que representa um membro do adapter do RecyclerView que precisa de um contexto fornecido pelo Android, sendo que oferecemos referência nula.

Chequemos como será seu comportamento se comentarmos o notifyDataSetChanged(). Desta forma, executando novamente com "Ctrl + Shift + F10", o teste funciona. Percebam que começamos a lidar com testes que exigem dependências das quais não temos muito controle.

Se criarmos um contexto manualmente, por exemplo, voltando à linha ListaLeilaoAdapter adapter = new ListaLeilaoAdapter(context: null) e substituindo null por new Context, um gigantesco contexto será criado, e então devemos remover o comentário de notifyDataSetChanged().

O teste falha novamente, pois estes testes dependem de outros componentes, ou são muitos complexos de serem criados, não são trivialmente executáveis, por conta de outros sets de unidade. Considerando este contexto, começaremos a aprender técnicas que permitem executarmos estes testes de modo mais fácil, sem comprometer o código fonte.

Com aquele trecho de código comentado, carregamos a app e nossa lista não aparece, isso comprova que existem grandes riscos nesses tipos de testes, e vamos aprender a lidar com eles!

Sobre o curso Testes no Android: Mocks e integrações

O curso Testes no Android: Mocks e integrações possui 169 minutos de vídeos, em um total de 54 atividades. Gostou? Conheça nossos outros cursos de Android em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Android acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas