Conceber para Testabilidade: Quebrar a Próxima Barreira
4 min de leitura

Na nossa série contínua DevOps for the Win, temos vindo a documentar a nossa jornada de transformação DevOps. No Capítulo 1, introduzimos o Triângulo DevOps: Ferramentas, Arquitetura e Pessoas no Desenvolvimento de Produtos, onde discutimos os elementos fundamentais que impulsionam o sucesso do DevOps. No Capítulo 2, explorámos Os Primeiros Passos na Nossa Transformação DevOps, partilhando como lançámos as bases para mudanças significativas.

Agora, no Capítulo 3, abordamos o próximo grande desafio que encontrámos: Conceber para a Testabilidade. Após melhorar a estrutura da nossa equipa, refinar a nossa Definição de Concluído e implementar a análise automatizada de código, percebemos que o principal gargalo no nosso pipeline se tinha deslocado para o design de software — especificamente, a testabilidade. O trabalho estava a acumular-se na fase de testes, impedindo-nos de alcançar ciclos de lançamento mais rápidos. Este capítulo aprofunda como superámos este desafio e melhorámos a nossa capacidade de entregar software de alta qualidade de forma eficiente.

O Gargalo dos Testes

A principal razão para este atraso foi o facto de os testes serem predominantemente manuais, focados em testes de UI e fluxos de trabalho do utilizador. Uma vez que a UI era geralmente um dos últimos itens a ser concluído, havia pouca ou nenhuma automação implementada, e os testes manuais tornaram-se a única abordagem viável. Casos de teste de UI automatizados (por exemplo, testes Selenium) eram tipicamente escritos após a conclusão dos testes manuais, principalmente para fins de regressão.

Esta dependência de testes manuais resultou em-

  • Atrasos significativos nos lançamentos de software.
  • A necessidade de múltiplas implementações para permitir testes paralelos por mais de um membro da equipa de QA.

Isto realçou a importância de tornar o código mais testável para otimizar o fluxo de valor. No entanto, alcançar isto revelou-se mais difícil do que tínhamos previsto.

Priorização de Testes de API e Testes Unitários

Para enfrentar este desafio, focámo-nos em duas áreas principais:

  1. Testes Unitários - Garantir que os componentes individuais pudessem ser testados de forma isolada.
  2. Testes de API - Reduzir a dependência de testes baseados na interface do utilizador (UI) e de dependências de base de dados.

Testes de API: Antecipar os Testes para um Feedback Mais Rápido

Redefinimos a nossa estratégia de testes de API com estas melhorias chave:

  1. APIs Bem Definidas- As APIs foram concebidas para serem simples, bem documentadas e disponíveis numa fase inicial do desenvolvimento.
  2. Evitar a Base de Dados como Ponto de Integração - A dependência de bases de dados para integração criava dependências que atrasavam os testes. Em vez disso, avançámos para uma integração baseada em API utilizando REST sobre HTTP e GraphQL. Isto minimizou o tempo de configuração da base de dados e melhorou a automatização dos testes.

Esta mudança reduziu significativamente os atrasos, permitindo uma automatização mais rápida e testes numa fase inicial, demonstrando que o foco em conceções API-first melhorou tanto a testabilidade como a eficiência.

Testes Unitários: Uma Mudança de Mentalidade

Inicialmente, os nossos esforços de testes unitários levaram a um rápido aumento da cobertura de testes, mas rapidamente identificámos problemas:

  • Alguns testes eram superficiais, escritos apenas para cumprir metas de cobertura de código.
  • Faltava-lhes uma validação significativa da funcionalidade, de casos-limite e de cenários de falha.

Para contrariar isto, salientámos:

  • Formar os programadores sobre como escrever testes valiosos.
  • Refatorização de código para melhorar a testabilidade.

Desafios na Escrita de Código Testável

O maior obstáculo para testes unitários eficazes foi a falta de testabilidade na própria base de código. Os principais problemas incluíam:

  • Funções grandes e monolíticas.
  • Componentes fortemente acoplados.
  • Fraca separação de responsabilidades.
  • Abstração insuficiente.

Estas questões dificultaram o isolamento e o teste eficaz de unidades individuais. Para resolver isto, nós:

  1. Diretrizes Fornecidas: Partilhámos as melhores práticas sobre:
    • Seleção de funções para testes unitários.
    • Refatorização de código para melhorar a testabilidade.
    • Conceber e usar mocks para facilitar os testes.
  2. Focados em Melhorias Incrementais: Os programadores foram incentivados a fazer pequenas e significativas alterações para melhorar a testabilidade ao longo do tempo.

Progresso Lento mas Constante

Apesar destes esforços, o progresso foi lento, especialmente para produtos legados. A arquitetura existente limitava o número de testes unitários que podíamos escrever. No entanto, estas ações não visavam apenas melhorar a base de código atual — eram um investimento no futuro:

  • Melhorar as competências dos programadores na conceção de código testável garantiu que projetos futuros não sofreriam dos mesmos problemas.
  • Melhorias incrementais evitaram interrupções enquanto aumentavam constantemente a cobertura da automatização de testes.
  • Uma mudança de mentalidade ajudou as equipas a ver a testabilidade não como um fardo, mas como uma necessidade para o sucesso sustentável do DevOps.

Para equipas e organizações que embarcam numa transformação DevOps, a testabilidade é uma área crítica de foco. Ajuda a aliviar os estrangulamentos na fase de desenvolvimento. No entanto, é importante gerir as expectativas — resultados imediatos podem não ser possíveis, especialmente ao lidar com grandes bases de código legadas. Refatorar esse código sem testes unitários e de regressão suficientes é um grande desafio.

Lições de The Pragmatic Programmer

Para cenários em que iniciar com uma nova base de código não é viável, The Pragmatic Programmer: Your Journey to Mastery, de Andy Hunt e David Thomas, oferece orientações acionáveis:

  • Focar em Alterações Incrementais: Concentre-se em refatorar partes pequenas e geríveis da base de código.
  • Desacoplar Componentes: Reduza as dependências para tornar as unidades individuais mais fáceis de testar.
  • Dividir Funções Monolíticas: Divida funções grandes em unidades mais pequenas e focadas.
  • Adotar um Design Modular: Tornar o código mais testável e fácil de manter, melhorando a modularidade.

A mensagem aqui é clara: uma vez que os constrangimentos iniciais tenham sido resolvidos, a testabilidade deve tornar-se uma área de foco principal. Isto irá aliviar as restrições na fase de testes e permitir uma entrega mais rápida de software de alta qualidade.

Perspetivas Futuras

A nossa jornada para melhorar a testabilidade ensinou-nos lições valiosas sobre o papel do design de software na viabilização de transformações DevOps suaves. Ao focarmo-nos na testabilidade, abordámos os gargalos, melhorámos as competências dos programadores e lançámos as bases para o sucesso futuro. Embora os resultados imediatos possam ser lentos, os benefícios a longo prazo em termos de qualidade, eficiência e crescimento da equipa tornam este investimento valioso.

À medida que continuamos a nossa jornada DevOps, medir o sucesso torna-se o próximo desafio principal. No Capítulo 4, iremos explorar como estabelecemos KPIs e Dashboards para acompanhar o nosso progresso e identificar áreas adicionais para melhoria.

Fique atento enquanto exploramos como a tomada de decisões baseada em dados nos ajudou a refinar os nossos processos DevOps e a otimizar o desempenho!

Subscrever o Blogue da Freyr

Política de Privacidade