Projetar para a testabilidade: Quebrando a próxima barreira
4 min ler

Na nossa série DevOps for the Win, temos estado a documentar a nossa viagem de transformação DevOps. No Capítulo 1, apresentámos o Triângulo DevOps: Ferramentas, Arquitetura e Pessoas no Desenvolvimento de Produtosonde discutimos os elementos fundamentais que impulsionam o sucesso do DevOps. No Capítulo 2, explorámos Os primeiros passos da nossa transformação DevOpspartilhando como lançámos as bases para mudanças significativas.

Agora, no Capítulo 3, abordamos o próximo grande desafio que encontramos: projetar para testabilidade. Depois de 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 havia mudado para o projeto de software — especificamente, a testabilidade. O trabalho estava a acumular-se na fase de testes, us alcançar ciclos de lançamento mais rápidos. Este capítulo aprofunda como superámos esse desafio e melhorámos a nossa capacidade de entregar software de alta qualidade com eficiência.

O estrangulamento dos testes

A principal razão para este atraso foi o facto de os testes serem predominantemente manuais, centrando-se nos testes da IU e nos fluxos de trabalho dos utilizadores. Uma vez que a IU era normalmente um dos últimos itens a ser concluído, havia pouca ou nenhuma automação, e os testes manuais tornaram-se a única abordagem viável. Os casos de teste automatizados da IU (por exemplo, testes Selenium) eram normalmente 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 nas versões de software.
  • A necessidade de múltiplas implementações para permitir testes paralelos por mais de um membro QA .

Isto realçou a importância de tornar o código mais testável para otimizar o fluxo de valor. No entanto, conseguir isso acabou por ser mais difícil do que tínhamos previsto.

Priorização de testes de API e testes de unidade

Para responder a este desafio, concentrámo-nos em duas áreas principais:

  1. Testes unitários - Garantir que os componentes individuais possam ser testados isoladamente.
  2. Testes de API - Reduzir a dependência de testes baseados na interface do utilizador e dependências de bases de dados.

Teste de API: Mudar para a esquerda para obter um feedback mais rápido

Redefinimos a nossa estratégia de teste de API com estas melhorias fundamentais:

  1. APIs bem definidas APIs APIs 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 a integração criou dependências que atrasaram os testes. Em vez disso, mudamos para a integração baseada em API usando REST sobre HTTP e GraphQL. Isso minimizou o tempo de configuração do banco de dados e melhorou a automação de testes.

Esta mudança reduziu significativamente os atrasos, permitindo uma automatização mais rápida e a realização de testes na fase inicial, demonstrando que a concentração em concepções que dão prioridade à API melhorou tanto a capacidade de teste como a eficiência.

Testes unitários: Uma mudança de mentalidade

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

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

Para contrariar esta situação, sublinhámos:

  • Educar os programadores sobre como escrever testes valiosos.
  • Refactoring do código para melhorar a testabilidade.

Desafios na escrita de código testável

O maior obstáculo a um teste unitário eficaz era 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.
  • Má separação das preocupações.
  • Abstração insuficiente.

Estes problemas dificultaram o isolamento e o teste eficaz de unidades individuais. Para resolver este problema, nós:

  1. Diretrizes fornecidas: Partilhámos as melhores práticas sobre:
    • Seleção de funções para testes unitários.
    • Refactoring do código para melhorar a testabilidade.
    • Conceber e utilizar simulações para facilitar os testes.
  2. Focado em melhorias incrementais: Os programadores foram encorajados a efetuar pequenas alterações significativas para melhorar a testabilidade ao longo do tempo.

Progresso lento mas constante

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

  • A melhoria das competências dos programadores na conceção de código testável garantiu que os projectos futuros não sofreriam os mesmos problemas.
  • As melhorias incrementais evitaram interrupções, ao mesmo tempo que aumentaram de forma constante a cobertura da automatização dos 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. Ela ajuda a aliviar os gargalos na fase de desenvolvimento. No entanto, é importante gerir as expectativas - resultados imediatos podem não ser possíveis, especialmente quando se lida com grandes bases de código legadas. A refacção desse código sem testes unitários e de regressão suficientes é um grande desafio.

Lições de O Programador Pragmático

Para cenários em que começar 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 práticas:

  • Alterações Incrementais: Concentre-se na refacção de partes pequenas e gerenciáveis da base de código.
  • Desacoplar componentes: Reduzir as dependências para tornar as unidades individuais mais fáceis de testar.
  • Dividir funções monolíticas: Dividir as grandes funções em unidades mais pequenas e mais específicas.
  • Adotar uma conceção modular: Tornar o código mais fácil de testar e manter, melhorando a modularidade.

A mensagem aqui é clara: uma vez resolvidos os estrangulamentos iniciais, a capacidade de teste deve tornar-se uma área de foco principal. Isto irá aliviar os constrangimentos na fase de teste e permitir uma entrega mais rápida de software de alta qualidade.

Olhando para o futuro

A nossa jornada para melhorar a testabilidade us ensinou lições us sobre o papel do design de software em possibilitar transformações DevOps tranquilas. Ao nos concentrarmos na testabilidade, resolvemos gargalos, aprimoramos as habilidades dos programadores e estabelecemos 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 fazem com que esse investimento valha a pena.

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

Fique ligado enquanto mergulhamos em como a tomada de decisões baseada em dados us ajudou us os nossos processos de DevOps e otimizar o desempenho!