Continuous Integration em projetos mobile (Android e iOS)

Guilherme Cherubini
Android Dev BR
Published in
8 min readJun 24, 2021

Quando trabalhamos em projetos de software, é comum que apareçam problemas de código no dia-a-dia, seja um novo bug relatado pelo time de QA, algum erro de build ou testes unitários não passando mais, etc. Ainda é mais comum que essas coisas aconteçam mais frequentemente quando trabalhamos com mais desenvolvedores no mesmo projeto.

Em busca da resolução dos problemas, a saga para tentar descobrir o que aconteceu no projeto começa. Nem sempre é fácil, ás vezes é até necessário verificar o histórico do git, realizar diferentes checkouts em diferentes commits pra entender o porquê de ter quebrado, quando e quem foi o autor por exemplo.

Você percebe que esse trabalho é um loop, que acontece dia após dia, e você está sempre dentro dele. Algo massante, chato e que te faz sempre perder bastante tempo.

O ato de automatizar seus processos e validações com técnicas de CI (Continuous Integration) pode te ajudar a detectar problemas mais rápido e identificar resoluções, assim como aumentar significativamente a qualidade do seu produto.

A integração contínua é uma prática de desenvolvimento de software em que os membros de uma equipe integram seu trabalho com frequência. Cada integração é verificada por um build automatizado (incluindo teste) para detectar erros de integração o mais rápido possível. Permite que uma equipe desenvolva software coeso mais rapidamente. — Martin Fowler

Este artigo apresenta uma proposta de arquitetura de Continuous Integration para projetos mobile, que pode servir tanto para Android quanto iOS. Pois, ambas plataformas, possuem técnicas e processos de qualidade parecidos e possuem a mesma saída: Um aplicativo.

Também, são demonstradas e esclarecidas algumas etapas de CI que podem ser integradas em um projeto mobile. Assim, é possível fazer uma análise e planejamento de uma arquitetura de CI, onde você conseguirá estruturar com a sua equipe e identificar quais passos realmente podem ser cruciais em seu contexto.

Então… Imagine que a cada commit, você tem o poder de validar alguns processos e etapas em seu projeto de forma automática.

Apresento um modelo de arquitetura de continuous integration para o seu projeto mobile:

Traduzindo, a cada commit realizado por um desenvolvedor no git em qualquer branch deverão ser executados os jobs paralelamente e concorrentemente dos estágios: Code Analysis, Build e Test.

Com essa separação de jobs, o desenvolvedor consegue identificar o que aconteceu de errado rapidamente caso algum estágio falhe, além de poder isoladamente analisar os logs de cada execução. Assim, um novo código deve ser escrito afim de corrigir os problemas evidenciados, que será validado mais uma vez pela pipeline.

A mesma validação deve acontecer quando você abre um PR/MR (pull request ou merge request), impossibilitando assim o merge da branch quando alguma etapa da pipeline está quebrando.

Exemplo de merge request sendo validado por uma pipeline de CI

Independente da ferramenta de CI que for escolhida em seu projeto (ex.: GitLab CI, Circle CI, Jenkins, etc.), é interessante aproveitar os benefícios de paralelismo e de cache de cada plataforma. Fazendo com que os jobs executem de forma paralela, concorrente e armazenando cache sempre que possível. Essa prática, trará mais velocidade à sua pipeline como um todo, trazendo uma experiência melhor para o desenvolvedor que estiver trabalhando no projeto. Pois assim, ele terá um feedback mais rápido por conta da velocidade de execução dos jobs.

Exemplo de pipeline com jobs concorrentes com a ferramenta Gitlab CI

A idéia de executar o mecanismo de CI em todos os branches é para que o software seja validado a todo momento, assim o desenvolvedor tem uma resposta rápida de como está a qualidade e o estado do código em todos os commits de todos os branches.

Caso sua infraestrutura não suporte tantas execuções, por conta de problemas de performance dos jobs e empilhamento de execuções das pipelines, você pode começar a pensar em modelos diferentes, mais simples e que fazem mais sentido para o seu contexto. Como, por exemplo: Executar a CI apenas na branch "master", "develop" ou apenas em branches que foram abertos merge/pull requests.

Abaixo, descrevo algumas etapas que podem ser relevantes em uma pipeline de aplicativos móveis:

01 — Estágio de Code Analysis

a. Lint

Etapa de processos rápidos que tem como objetivo analisar o código estaticamente em busca de erros de formatação.

Está é uma etapa relevante para adicionar na sua pipeline de CI, pois é rápida e já te traz benefícios de padronização de código entre todos os desenvolvedores.

Ambas bibliotecas explicadas abaixo possuem uma fácil instalação e integração com ferramentas de CI e possibilitam a quebra da pipeline sempre que houverem novas violações.

KTLint - Android

KTLint é uma ferramenta que analisa a formatação do seu código e acusa erros sempre que houverem inconsistências. Através dele você consegue ter um padrão de formatação de código de forma rápida entre todos os membros da equipe, sem muitas discussões, pois ele já segue diversas regras recomendadas pelo kotlinlang.org e Android Kotlin Style Guide.

SwiftLint - iOS

Assim, como a ferramenta acima. O SwiftLint segue a mesma lógica e tem a mesma finalidade de verificar estilo e convenções do código pra quem trabalha com iOS, seguindo o GitHub Swift Style Guide.

b. Code Smells

Etapa importante para você dedicar à busca e identificação de problemas relacionados ao código que pode ser suscetível a erros, que pode conter code smells.

Existem ferramentas no mercado que possibilitam que seja feita uma varredura estática de código, que possuem funcionalidades que irão analisar o código e gerar relatórios alertando sobre possíveis vulnerabilidades e imperfeições.

Detekt — Android

A ferramenta Detekt analisa um código Kotlin estaticamente em busca de code smells, código complexo, má formatação e muitas outras coisas. Ela pode ser configurada facilmente em uma pipeline de CI, e viabiliza que sua pipeline quebre caso o número de issues for maior que um número específico. Com certeza se utilizada corretamente e se incluída de forma gradual, pode trazer muitas melhorias de qualidade ao seu produto.

SonarQube — Android e iOS

Ferramenta muito completa, que pode ser incluída tanto para projetos Android e iOS que consegue analisar o código e gerar muitos relatórios que abordam a qualidade do seu código em modo geral: complexidade, possíveis vulnerabilidades, code smells, segurança, cobertura de testes, etc.

Ferramenta bem mais complexa comparada ao Detekt, que demanda mais tempo no setup e configuração.

02 — Estágio de Build

Esse estágio é muito importante, principalmente se você trabalha com outros membros em sua equipe, pois a cada commit você saberá se o seu projeto continua compilando corretamente e se o seu build está passando ou não. Sabendo disso, você consegue identificar mais rápido quando e porque aconteceu o problema já que você consegue relacionar o commit em questão e o autor nas plataformas de CI, como também analisar os logs desse comando executado.

a. Build Debug

Você pode escolher a variante que quer adicionar a sua pipeline, uma sugestão seria rodar a cada commit sempre a variante de "debug", pois ela é mais rápida e você conseguirá rapidamente identificar problemas de build e compilação.

b. Build Release

Outra sugestão, seria adicionar na sua pipeline a variante de "release" a cada commit. Essa seria uma etapa opcional, pois demanda mais tempo e você só garantirá algumas coisas a mais da que de debug. Como por exemplo, se o ofuscador do seu código passou, se o processo de redução de código passou, ou quaisquer processos a mais que você tenha configurada nessa etapa de "release". Eu particularmente prefiro deixar essa etapa de "build" de
"release" apenas quando eu quero distribuir automaticamente meu aplicativo, na parte de "Continuous Delivery". Mas se você achar interessante a validação a cada commit na etapa de CI, fica a dica também.

03 — Estágio de Testes

a. Unit Tests

Como já sabemos, é fundamental testarmos nosso software e adicionar essa etapa ao nosso sistema de CI é algo muito coerente, pois a cada "commit" certificaremos que nenhuma funcionalidade parou de funcionar ou se existe algum novo bug em desacordo com as especificações do projeto.

Code covered with tests is more reliable than the code without. If a future change breaks something in the code, developers will be able to identify the root of the problem right away rather than coming through an unwieldy codebase to find the issue.

b. UI Tests

Nem sempre, desenvolvedores criam testes desta origem, por exigirem um emulador/simulador, demorarem mais para ser executados e poderem ser testes flaky. Testes que normalmente testam os cenários reais de que um usuário passaria clicando nos botões e navegando entre as telas via interface.

Segundo a pirâmide de testes, testes desse tipo devem ser priorizados depois que você tem uma base sólida de "testes unitários" e de "integração". Ainda assim, são testes importantes quando você pensa em ter uma regressão geral do seu aplicativo.

Algumas equipes possuem pessoas que trabalham especificamente nesses testes através de ferramentas "Cloud Based", que é o caso da ferramenta BrowserStack por exemplo, que é uma ótima ferramenta que te permite testar na nuvem seu novo código através de vários devices diferentes pré-definidos.

Snapshot/Screenshot Tests

Iterar pelas telas do aplicativo em busca de problemas visuais não é uma tarefa trivial. Com esse tipo de teste, você pode previnir que o layout das suas telas não mudem sem planejamento através da validação de screenshots de como as telas eram antes e de como elas vão ser logo após um commit.

Pode ser interessante adicionar na sua pipeline de CI essa etapa, fazendo com que ela falhe sempre que forem percebidas diferenças de layouts não planejadas.

Para mais detalhes, você pode consultar algumas ferramentas que realizam esse tipo de teste tanto no Android quanto no iOS. Como por exemplo: Swift Snapshot testing, Android Screenshot Testing.

Code Coverage

Uma prática legal para você começar a tentar trazer mais qualidade ao seu produto é adicionar a execução de alguma ferramenta de cobertura de testes na sua pipeline, realizando sempre o upload dos relatórios de cobertura a cada "commit". Assim, é possível fazer um acompanhamento da evolução da sua estratégia de testes.

Outra idéia é fazer a pipeline quebrar sempre que a cobertura de testes unitários do código estiver menor que um valor pré-determinado, por exemplo: 80%. Isso faz com que desenvolvedores se preocupem em não baixar esse limite a cada commit. Porém lembrem-se que cobertura de testes não é tudo, mas sim testar bem o software.

Uma ferramenta que pode auxiliar nessa etapa no Android é o próprio Android Studio, Jacoco ou o Sonar. Já no iOS pode se utilizar o XCode mesmo ou o Sonar.

— Conclusão

A vida com um computador trabalhando e verificando coisas pra você é mais tranquila, te traz tempo para estudar outras coisas ou para um momento de café. A utilização de automatizações de CI alinhadas com boas práticas de programação trazem mais qualidade ao seu código e te possibilitam encontrar e corrigir problemas mais rapidamente.

Espero que esse post tenha acrescentado em algo, e que te de insumos para realizar uma estratégia de CI voltada ao seu contexto.

E você conhece outras etapas relevantes em um processo de Continuous Integration no contexto Mobile?

Comente o que pensa sobre e como vem estruturando sua pipeline!

--

--

Guilherme Cherubini
Android Dev BR

I’m a mobile developer passionate about programming, software architecture, testing and music!