Testando sua aplicação

Criando repositórios testáveis

Thiago Souto
Android Dev BR
Published in
4 min readMar 16, 2021

--

photo by @craftedbygc on Unsplash

Introdução

Repositórios são classes que tem como objetivo isolar a camada de acesso de dados na sua aplicação, isto é, permitir acesso a fontes de dados remota, locais e cache. Quando falamos em dados locais, também estamos falando de contatos de telefone, imagens, etc… O objetivo desse post é demonstrar a criação de testes para essa camada tão importante, que é a ponte entre o acesso a diferentes tipos de dados e a camada de apresentação.

Como criar testes para o seu repositório?

Primeiro precisamos entender a estrutura de um repositório, se formos aplicar para o nosso cenário onde precisamos acessar uma API do retrofit, então nosso repositório vai acessar apenas a camada de dados remota, sendo apenas um "get". Esse é o momento que muita gente se pergunta: se vamos acessar apenas dados remotos de uma única API, então por que vamos criar um repositório para ser apenas esse "get"?

diagrama em que o repositório implementa MoviesApi (retrofit) diretamente

Analisando esse diagrama, um dos poucos ganhos que podemos perceber é: Isolar na camada de dados o acesso a API, que no caso, está sendo implementada com retrofit. Esse repositório protege o ViewModel/UseCase de certos tipos de alterações, todavia, o repositório ainda fica acoploado a dois pontos: Um Framework e a uma implementação específica.

Com o princípio da inversão de dependência, podemos isolar o framework e a implementação concreta através de um contrato, permitindo maior flexibilidade para o nosso repositório, exemplificado no diagrama abaixo:

imagem mostrando o principio da inversão de dependência entre o repositório e a fonte de dados

Ao olharmos esse diagrama, percebemos que podemos injetar a implementação referente ao contrato de RemoteDataSourceno repositório, isto é, o MoviesApifoi isolado em uma extremidade e sua comunicação com o repositório é realizada unicamente através do contrato de RemoteDataSource, gerando desacoplamento com a implementação específica e o framework do retrofit. Esse isolamento possibilita a troca de implementações da RemoteDataSource sem o risco de quebrar o repositório, reduzindo o impacto dessa modificação.

Mão na massa

Vamos criar o nosso Model, Movie.

gist com classe Movie

Com nosso model criado, precisamos criar o contrato de acesso a dados remotos, no caso, RemoteDataSource.

gist com a interface RemoteDataSource

Partindo do TDD, vamos criar nosso teste:

gist com o teste para o repositório

Note que estou injetando o MoviesApi no construtor da classe. A injeção do mock da classe MoviesApi com o treino para o mock retornar o objeto esperado dispensa a necessidade do servidor ligado. A classe MoviesApi já foi testada com o MockWebServer, então não é necessário efetuar nenhuma validação em seu comportamento aqui nesse teste.

Dica: Sempre que possível, injete a dependência através do construtor, pois isso reduz o acoplamento e facilita os testes. Evite o acoplamento diretamente nas classes como o comando do dagger component.inject(this) ou o component.get() do koin.

Após criar as classes para viabilizar a compilação do teste, o teste falhou devido a ausência de implementação das regras.

Implementando nosso remote data source, fazemos uma chamada para API e retornamos a transformação do objeto para List<Movie>.

classe RetrofitRemoteSourceImpl acessando a RetrofitRemoteSourceImpl

É muito importante não vazar detalhes da implementação do MoviesApi , isto é, o objeto TopRatedMoviesResponse não pode ter sua visão fora do pacote remote, pois isso é relacionado apenas a source remota do retrofit. Executamos o teste e agora o feedback foi de que o teste passou:

screenshot com o resultado da execução do teste, no caso, teste executado com sucesso

Precisamos criar um repositório que será responsável por administrar as cahmadas para o data source. Para facilitar o entendimento do teste, encapsularemos dentro de uma estrutura de given-when-then.

teste para a chamada do repositório

Olhando para a estrutura do teste, entendemos que:

  1. Criamos um mock da chamada realizada pelo RemoteDataSource com o intuito de controlar o resultado esperado pela execução do método.
  2. Executamos o método a ser testado pelo repositório, o getTopRatedMovies.
  3. Validamos o resultado retornado pelo método.

Com isso, criamos as classes para fazer o código compilar e implementamos o repositório chamando o RemoteDataSource.

classe implementando o MoviesRepository

Pronto, nosso repositório foi implementado sem nenhum acoplamento com a classe MoviesApi. O código ficou simples e quando precisarmos implementar cache ou algo do tipo, nossa alteração não vai impactar o código da MoviesApi e vamos ter os testes automatizados para validar o comportamento implementado anteriormente.

Conclusão

Esse post mostrou como em poucos passos podemos criar um repositório e a camada de acesso a dados remota de forma simples, desacoplada e testáveis.

Gostou do post? Achou interessante? viu algum ponto de melhoria? Comente ou me envie uma mensagem!

Esse foi o segundo post da serie "Testando sua aplicação", onde tem como objetivo mostrar a criação de componentes de arquitetura importantes para o desenvolvimento android junto com seus testes.

Fontes

  1. https://developer.android.com/codelabs/kotlin-android-training-repository#5
  2. http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
  3. https://martinfowler.com/bliki/GivenWhenThen.html

--

--