Melhores Práticas em Testes para Equipes Scrum: Garanta Lançamentos Estáveis!

Foto do autor

By luis

Um processo Scrum sem um plano de testes adequado equivale a uma Prova de Conceito (POC) turbinada.

Hoje em dia, grande parte dos projetos bem-sucedidos inicia-se com uma POC, que consiste numa avaliação inicial de uma ideia. Nesta fase, a tecnologia e os recursos escolhidos são analisados, avaliando-se o impacto potencial nos utilizadores de negócio. Se a POC demonstrar que a ideia é viável e merece investimento, forma-se uma equipa de projeto completa para desenvolver e implementar o produto com todas as funcionalidades em ambiente de produção.

Este cenário é ideal para uma equipa Scrum. A equipa consegue desenvolver rapidamente o protótipo, adicionando novas funcionalidades a cada sprint. Os utilizadores de negócio podem acompanhar o progresso em tempo real, observando como a ideia se materializa em apenas alguns sprints. Embora cause uma forte impressão (que é o objetivo principal da POC), há uma lacuna significativa: as atividades de teste são mínimas ou inexistentes. Pensar no processo de teste pode até ser considerado uma piada.

Não é uma atividade popular numa equipa Scrum, e as pessoas tendem a preferir desenvolver sem processos que as atrasem. E é isso que qualquer atividade de teste parece fazer. Ninguém quer atrasos desnecessários quando o objetivo é impressionar o utilizador final.

O resultado de uma equipa de projeto que continua a trabalhar da mesma forma após o período da POC é o que chamo de “POC com esteroides” – um sistema de produção que cresce em tamanho e funcionalidades, mas que continua a comportar-se como uma POC, um produto em desenvolvimento com falhas ocultas e casos extremos não explorados, à espera de um problema grave.

Antes de chegar a esse ponto, a equipa terá cada vez mais dificuldades em manter as entregas estáveis a cada sprint, pois a correção de problemas constantes torna-se cada vez mais complexa.

A seguir, apresento algumas técnicas que considero as melhores práticas para implementar processos de teste robustos, sem sobrecarregar o progresso do desenvolvimento. Afinal, é isso que todos queremos como equipa Scrum.

Dividir a carga

Onde mais devemos começar ao lidar com problemas desnecessários senão dividindo a carga?

O que quero dizer com isto é: criar um plano que exija uma pequena atividade adicional por parte dos programadores, mas que contribua significativamente para o objetivo comum ao longo do tempo, de forma gradual, mas consistente.

#1. Desenvolver testes unitários para cada parte do novo código

Se conseguir convencer as suas equipas Scrum a incluir no seu critério de “Concluído” o desenvolvimento de testes unitários para cada novo código criado dentro do sprint, isso será uma grande conquista a longo prazo.

As razões são bastante óbvias:

Isto forçará os programadores a pensar em diversos caminhos não convencionais do código.

  • Estes testes unitários podem ser integrados em pipelines DevOps automatizados e executados em cada implantação no ambiente de desenvolvimento ou teste. Além disso, as métricas do pipeline podem ser facilmente exportadas e utilizadas para demonstrar aos utilizadores de negócio a percentagem de cobertura de casos de teste diretamente ligados ao código fonte.

O mais importante é começar o quanto antes. Quanto mais tarde os testes unitários começarem a ser desenvolvidos, mais difícil será para os programadores adicioná-los a um sprint.

  • Será necessário um esforço considerável para desenvolver testes unitários para o código já existente. Algumas partes do código podem ter sido criadas por outro programador e o conhecimento exato de como deve funcionar em cada parte do código não é claro para o programador atual. Em alguns casos, pode chegar a tal ponto que adicionar um teste unitário ao código alterado demore mais tempo do que desenvolver a alteração de funcionalidade para o sprint (um estado que ninguém contabilizou durante o planeamento do sprint, certamente).

#2. Criar uma rotina de execução de todos os testes unitários no ambiente de desenvolvimento

Antes mesmo de criar um pedido de “pull” para juntar o novo código ao “branch” principal, deve ser padrão testar o código de funcionalidades e os testes unitários no ambiente de desenvolvimento. Ao fazer isto, garante que:

  • O código do teste unitário está realmente a funcionar para cada parte (no final, é apenas mais código que deve ser verificado). Este passo é muitas vezes completamente ignorado. Assume-se, de alguma forma, que, se o teste unitário estiver ligado ao pipeline DevOps, será eventualmente executado e testado em algum lugar por defeito. No entanto, isto não é mais do que transferir os problemas para os níveis superiores das atividades do sprint, onde a equipa geralmente tem menos tempo e mais stress para terminar cada tarefa em que se comprometeu.
  • O novo código de funcionalidade é testado pelo programador em relação à funcionalidade básica. Claro que não se pode esperar que este teste verifique completamente a funcionalidade de negócio, mas pelo menos este teste irá confirmar que o código se comporta da forma como o programador pretende (ignorando, por agora, o facto de que a lógica de negócio pode ter sido interpretada incorretamente durante o desenvolvimento).

#3. Esperar a execução do teste unitário após a junção do código ao “branch” principal

Uma coisa é ter código funcional no “branch” local (onde ninguém, exceto o proprietário do branch, está a desenvolver uma nova funcionalidade), mas é completamente diferente ter o mesmo código a funcionar após o pedido de “pull” e a junção ao “branch” principal.

O “branch” principal contém alterações de outros membros da equipa Scrum e, mesmo que os conflitos sejam resolvidos e tudo pareça bem, o código após a junção e a resolução de conflitos continua a ser basicamente um código não testado e é arriscado enviá-lo adiante sem verificar primeiro se ainda está a funcionar bem.

Assim, o que se comprovou eficaz aqui é simplesmente pedir para executar o mesmo teste unitário – já feito anteriormente no ambiente de desenvolvimento – também no ambiente que é construído a partir da versão do código do “branch” principal.

Para os programadores, pode ser mais um passo que precisam de incluir nas suas rotinas, mas não é um grande esforço, uma vez que, neste caso, não é preciso inventar nada de novo – basta repetir o que já foi feito uma vez.

Agora, este passo pode ser ignorado num caso particular:

  • Os testes unitários automatizados nos pipelines DevOps são tão abrangentes que já cobrem todo o teste manual executado acima disso.

Embora atingir este estado seja certamente viável, nunca experimentei tal estado na vida real. Provavelmente, seria demasiado demorado para os programadores criar testes unitários automatizados tão detalhados. Pode facilmente ser inaceitável para o proprietário do produto permitir que a equipa dedique tanto tempo a esta atividade, pois isto teria um impacto explícito no número de tarefas entregues num sprint.

No entanto, a preferência pelo conteúdo do sprint nunca deve ser uma desculpa para não efetuar os testes unitários, ou mesmo para os minimizar. Ao fazer isto, a equipa voltará a um estado em que o código tem pouca cobertura de testes unitários e, para recuperar o atraso, pode ser demasiado tarde (mais uma vez, maior esforço para a conclusão dos testes unitários do que a alteração real do código para um sprint).

Por todas estas razões, eu recomendaria a repetição dos testes unitários na versão do código principal sem hesitações. É um esforço tão baixo em comparação com o valor que irá trazer.

Fará a verificação final da preparação do “branch” principal para a fase de testes de lançamento. Além disso, ajudará a encontrar a maioria dos bugs técnicos (por exemplo, bugs que impedem que o código fonte seja executado com sucesso até ao final bem-sucedido), deixando para a fase seguinte a tarefa de se concentrar apenas na verificação da funcionalidade do negócio.

Preparar para testes funcionais

Todas as atividades de teste anteriores devem levar a uma conclusão específica – o código do “branch” principal está livre de bugs técnicos e executável sem problemas para fluxos funcionais completos.

Esta é uma fase que pode ser facilmente realizada por apenas uma pessoa, e esta pessoa nem precisa de ter formação técnica.

Na verdade, é muito melhor se for realizada por alguém desconectado dos programadores, mas com consciência funcional de como os utilizadores de negócio esperam que o código se comporte. Existem duas atividades principais a concluir:

#1. Teste funcional direcionado a novas tarefas do sprint

Cada sprint deve, idealmente, trazer alguma nova funcionalidade (incremento do objetivo do sprint) que não existia antes e, portanto, deve ser verificada. É importante garantir que o novo software funciona de tal forma que os utilizadores de negócio fiquem satisfeitos por o terem agora, uma vez que, obviamente, ansiavam por ele durante todo o último sprint, no mínimo :).

É uma experiência tão triste quando a (longa) funcionalidade prometida é finalmente anunciada para ser lançada, apenas para se descobrir no dia seguinte que ela não funciona bem.

Por conseguinte, testar adequadamente a nova funcionalidade do sprint é crucial. Para que isto seja bem-sucedido, é boa prática reunir com antecedência casos de teste importantes com as partes interessadas relevantes (o proprietário do produto ou mesmo os utilizadores finais) e fazer uma lista de todos os casos de teste necessários para o conteúdo dentro do sprint.

Isto pode parecer esmagador à primeira vista, mas, pela minha experiência, é novamente totalmente gerível por uma única pessoa. Como os sprints são, na sua maioria, bastante curtos (como um período curto de duas semanas), quase nunca há muito conteúdo novo, uma vez que o sprint também contém atividades adicionais, tais como tarefas de dívidas técnicas, documentação, tarefas de design/spike, etc.

Os casos de teste são executados com o objetivo de verificar principalmente a funcionalidade desejada. Se ocorrer algum problema com os resultados, apenas o programador relevante será contactado (aquele que possui as alterações relacionadas com esse defeito específico) para resolver o problema rapidamente.

Desta forma, os programadores passarão um tempo mínimo ligados aos testes funcionais e ainda poderão concentrar-se nas atividades de que mais gostam.

#2. Execução de casos de teste de regressão

A outra parte do teste funcional deve ser garantir que tudo o que funcionou até agora também funcionará após o próximo lançamento. É para isso que servem os testes de regressão.

Os casos de teste para testes de regressão são melhores se forem mantidos regularmente e revisitados antes de cada lançamento. Com base nas especificidades concretas do projeto, é melhor mantê-los simples, mas abranger a maioria das funcionalidades básicas e importantes fluxos de ponta a ponta que percorrem todo o sistema.

Normalmente, cada sistema tem tais processos que tocam em muitas áreas diferentes, estes são os melhores candidatos para casos de teste de regressão.

Em alguns casos, os testes de regressão cobrem também implicitamente novas funcionalidades no sprint, por exemplo, se a nova tarefa no sprint estiver a alterar alguma parte específica do fluxo existente.

Por conseguinte, na maioria dos casos, não é muito complexo concluir testes de regressão juntamente com novos testes de funcionalidade de tarefas do sprint, especialmente se isto for feito regularmente antes de cada lançamento de produção e a periodicidade dos lançamentos de produção não for muito pequena.

Insistir na execução de testes de garantia de qualidade antes de cada lançamento de produção

O teste de Garantia de Qualidade (QA) é o passo final antes da libertação para produção e, muitas vezes, este teste é ignorado por não ser importante. Especialmente se a equipa Scrum for gerida de forma agressiva para novos conteúdos.

Mesmo os utilizadores de negócio dirão que estão interessados em novas funcionalidades e não tanto em preservar a funcionalidade ou manter baixo o número de defeitos. Mas, mais uma vez – com o tempo, esta é a principal razão pela qual a equipa de desenvolvimento terá de abrandar eventualmente e, então, os utilizadores de negócio não receberão o que pedem de qualquer forma.

O teste QA deve ser executado exclusivamente por pessoas fora da equipa Scrum, idealmente pelos próprios utilizadores de negócio num ambiente dedicado, que seja o mais próximo possível da produção futura. Em alternativa, o proprietário do produto pode substituir aqui os utilizadores finais.

Em qualquer caso, este deve ser um teste limpo e funcional da perspetiva do utilizador final, sem qualquer ligação à equipa de desenvolvimento Scrum. Apresentará uma visão final do produto e será utilizado de uma forma que possivelmente ninguém da equipa Scrum antecipou (não é um caso ideal, mas pode definitivamente acontecer). Ainda há tempo para correções de última hora.

Em alternativa, pode ficar claro que as expectativas não foram bem compreendidas pela equipa Scrum e, nesse caso, pelo menos podemos concordar com um plano de acompanhamento, ainda muito antes do lançamento da produção real. Mais uma vez, este não é um caso ideal, mas ainda é muito melhor do que admitir a falha logo após relatar um lançamento de produção bem-sucedido.

Onde ir daqui?

A aplicação destas práticas ao trabalho diário do Scrum pode conduzir a hábitos de lançamento de sprint mais estáveis e previsíveis, sem atrasar os lançamentos de produção ou gastar o sprint inteiro apenas a preparar o próximo lançamento, ou mesmo sem forçar todos na equipa Scrum a fazer algo que realmente não gostam ou não sabem como fazer de forma eficaz na maior parte do sprint.

Mas, certamente, não é preciso parar aqui.

A inclusão do teste de desempenho é um tópico a referir aqui. Estes são frequentemente ignorados ou considerados desnecessários, sendo excluídos na primeira ronda de “otimização das atividades do Scrum”. No entanto, sempre tive dúvidas sobre como esperar que o sistema de produção evolua com o tempo se não for verificado regularmente quanto ao desempenho.

Subir um nível significa também a introdução de testes automatizados.

Já abordei um grupo de testes automatizados (testes unitários em curso). No entanto, pode desenvolver testes completos de regressão de ponta a ponta totalmente automatizados e executados por eles próprios após cada implantação no ambiente de teste. Isto libertaria ainda mais a equipa de Scrum de desenvolvimento, mas requer uma equipa dedicada para desenvolver e manter estes testes automatizados. Este tornar-se-ia um trabalho constante, uma vez que, sempre que o código de produção muda, alguns testes automatizados existentes podem tornar-se inválidos e, por conseguinte, devem ser atualizados para continuar a funcionar nos pipelines. É um esforço que apenas alguns estão dispostos a pagar, mas os benefícios para a equipa de desenvolvimento Scrum seriam enormes.

Estes são tópicos que vão muito além do âmbito deste artigo e que descobrem o cronograma e o tempo certos para cada tipo de teste aqui mencionado – para que o possa fazer dentro de uma janela de sprint. Ficarei feliz por me aprofundar na próxima vez!