A importância do ambiente de mock em sua aplicação

Neste artigo, vamos abordar alguns pontos importantes para criar esse ambiente de mock em sua aplicação Android.

Vamos explorar o uso de flavors, injeção de dependência e interfaces para facilitar a criação de um build variant de mock.

Durante o desenvolvimento de uma aplicação, é comum encontrarmos alguns bloqueios pelo caminho, o que pode ser um grande problema quando temos um prazo a cumprir. Um dos obstáculos mais comuns é a indisponibilidade de um serviço de API, seja devido a quedas momentâneas do ambiente ou à necessidade de aguardar a publicação da API. Essa situação pode travar nosso desenvolvimento, já que nosso fluxo depende da chamada de serviço.

E se tivéssemos a possibilidade de ter um ambiente de mock em nossa aplicação, para que, nessas situações, pudéssemos continuar executando o aplicativo normalmente de forma simples?

1. Flavors: Separando os ambientes

Uma abordagem recomendada para separar os ambientes em sua aplicação é o uso dos Product Flavors. Então o que seriam exatamente esses Flavors? Trata-se de uma maneira de gerar várias versões customizadas de um aplicativo a partir do mesmo projeto, permitindo ter versões para desenvolvimento, produção e mock, sem a necessidade de criar projetos separados para cada uma delas.

Para isso, deve editar o arquivo buid.gradle localizado no diretório app. Exemplo:

mock API

Aqui está um exemplo de como utilizar Product Flavors no arquivo build.gradle:

Ao declarar os Product Flavors, estamos definindo diferentes ambientes para nosso aplicativo: prod (produção) e mock. Utilizamos a propriedade `dimension` para agrupá-los em uma dimensão com o nome “environment”. Além disso, podemos adicionar sufixos ao `applicationId` de cada flavor por meio da propriedade `applicationIdSuffix`, permitindo identificadores de aplicativo exclusivos para cada ambiente.

2. Injeção de Dependência com Koin

Para criar um build variant de mock eficiente, utilizaremos o framework Koin para a injeção de dependência em nossa aplicação. A injeção de dependência é uma técnica que nos permite fornecer implementações diferentes para interfaces específicas, dependendo do contexto ou ambiente em que estamos executando o aplicativo.

Primeiro, precisamos configurar o Koin em nosso projeto. No arquivo build.gradle do módulo do aplicativo, adicionamos a dependência do Koin:

3. Utilizando Interfaces e Conceitos do SOLID

Para criar um build variant de mock flexível e modular, é recomendado utilizar interfaces e seguir os princípios do SOLID. Ao definir interfaces para as dependências da API, podemos ter diferentes implementações para cada ambiente, incluindo as classes de mock. Por exemplo, suponha que tenhamos uma interface `ApiService` que representa a comunicação com a API externa. Podemos criar uma implementação real para o ambiente de produção e uma implementação para o ambiente de mock.

Qual o princípio do SOLID estamos falando aqui?

Ao definir a interface ApiService e suas implementações ProductionApiService e MockApiService, estamos aplicando o princípio da Inversão de Dependência. A classe que depende da comunicação com a API (classe de alto nível) não está acoplada diretamente às implementações concretas (classes de baixo nível), mas sim à abstração da interface ApiService.

Essa abordagem permite que diferentes implementações sejam usadas e injetadas sem afetar a classe que as utiliza.

Dessa forma, a inversão de dependência possibilita uma maior flexibilidade e modularidade no código. No contexto do build variant de mock, podemos alternar entre as implementações ProductionApiService e MockApiService com base no ambiente em execução (Flavor), sem modificar diretamente a classe que as utiliza.

Isso facilita o desenvolvimento quando a API está indisponível, pois podemos injetar a implementação de mock que simula o comportamento da API ausente.

Sendo assim, um exemplo de código:

 

4. Implementação do Koin e do Retrofit

Então, agora que já possuímos um “esqueleto”, podemos construir nossa requisição. Aqui, estaremos utilizando o Retrofit, que permite definir as chamadas à API por meio de interfaces. Dessa forma, não é necessário criar uma classe para o ambiente de produção, uma vez que entendemos que a interface do Retrofit já se destina a esse Flavor.

Com isso, podemos compreender que o UserApiService é a interface que implementa as chamadas do Retrofit, enquanto a MockApiService passa a implementar a interface de produção. Considerando que o Retrofit já está configurado no projeto, não abordaremos a parte de build neste artigo.

Agora que temos uma implementação básica no Retrofit, podemos explorar como podemos usar o Koin em conjunto com os Flavors e a função `resolveRetrofit` para resolver as dependências corretas do serviço de API em diferentes variantes de build.

No Koin, podemos definir nossos módulos de injeção de dependência e fornecer as implementações apropriadas com base nas variantes de build. Vamos dar uma olhada em como isso pode ser feito.

Primeiro, criarmos a função `resolveRetrofit` onde verificamos se o Flavor é de produção para criar o Service do Retrofit.

Agora vamos criar um repository que é onde irá buscar os dados, seja da API ou do mock. Lembrando do conceito de SOLID aplicado nesse artigo, o repository não precisa saber de onde vem o dado, sendo que ele só conhece a UserApiService.

Por último, devemos criar o módulo do Koin e utilizar a função `resolveRetrofit` para injetar o service ideal para o ambiente dentro do `UserApiRepository`. Faremos isso da seguinte forma:

Nesse módulo, utilizamos a função `single` para definir a injeção de dependência do `UserApiService`. Dentro dessa função, chamamos a função `resolveRetrofit()` para verificar o ambiente em execução e fornecer a implementação adequada. Se o ambiente for de mock, retornamos `null`, o que indica que o Koin deve utilizar a implementação de `MockApiService`. Caso contrário, o Koin resolverá a dependência utilizando a implementação de produção do serviço de API. Em seguida, utilizamos a função `factory` para definir a injeção de dependência do `UserApiRepository`. Nesse caso, passamos o argumento `get()` para obter a instância do `UserApiService` que será injetada no repositório.

Com o módulo de injeção de dependência definido, precisamos realizar a inicialização do Koin em nossa aplicação. Isso pode ser feito no método `onCreate()` da classe `Application` ou em qualquer outra classe que seja responsável pela inicialização do Koin. Vamos supor que a inicialização seja feita na classe `MyApplication`:

API mock

No exemplo acima, utilizamos o método `startKoin` para iniciar o Koin. Passamos o contexto da aplicação como argumento para `androidContext()` e especificamos o módulo `userModule` que definimos anteriormente.

Por fim, precisamos fazer a declaração do `MyApplication` no arquivo `AndroidManifest.xml` para que o Koin possa inicializar corretamente:

mock

Com essas configurações, o Koin estará configurado em sua aplicação Android, e a injeção de dependência será realizada conforme definido no módulo `userModule`. O serviço de API apropriado será injetado no repositório com base no ambiente em execução, permitindo a utilização do ambiente de mock quando necessário.

 

5. Utilizando o ambiente de mock

Agora que finalizamos a configuração do ambiente de simulação, podemos incorporá-lo à nossa aplicação Android. Durante o desenvolvimento ou nos momentos em que a API de produção não estiver acessível, é possível alternar para esse ambiente para gerar dados simulados. Para realizar essa mudança, basta escolher essa opção no Android Studio antes de compilar e executar o aplicativo.

No canto inferior esquerdo da janela do Android Studio, você encontrará a barra “Build Variants”. Ao clicar nela, selecione a opção de simulação para o modo de execução desejado. Ao optar por essa configuração, o aplicativo utilizará uma implementação que gera dados simulados para as chamadas de API, permitindo que o desenvolvimento prossiga sem interrupções, mesmo quando a API de produção não estiver disponível.

Em resumo, um ambiente de mock desempenha um papel fundamental no desenvolvimento de aplicativos, permitindo que você avance mesmo quando os serviços de API estão indisponíveis. Através do uso de Product Flavors, você pode separar claramente os ambientes em sua aplicação, como produção, desenvolvimento e mock, facilitando o gerenciamento e a configuração adequada de cada um. Além disso, a criação de classes de mock oferece flexibilidade e controle sobre o comportamento simulado dos serviços de API, proporcionando um fluxo de trabalho contínuo e a possibilidade de testes abrangentes.

Ao adotar o ambiente de mock em sua aplicação, você ganha agilidade, robustez nos testes e a capacidade de antecipar problemas antes mesmo da disponibilidade total dos serviços reais.