Como manter a qualidade do desenvolvimento de software

Uma entrega de desenvolvimento com qualidade é aquela que atende os requisitos solicitados em um prazo razoável. Mas, sabemos que existem desafios para se atingir e manter essa qualidade até o final do projeto. Nesse artigo, vamos compreender quais são esses desafios e o que fazer para superá-los.

Manoel Souza

Manoel Souza

November 05, 2020 | leitura de 10 minutos

dev

Hoje consideramos que a qualidade sentida e percebida pelo cliente se dá pelo resultado da entrega, ou seja, se atende aos requisitos e se o prazo orçado foi respeitado. Mas, também é interessante medir esse resultado e avaliar o que pode ser melhorado.

Para traçar esse desafio, nos deparamos com algumas funções sendo criadas no mercado na tentativa de melhorar a qualidade do desenvolvimento. O designer "fala" com os usuários e faz a ideação do produto; o desenvolvedor recebe esse levantamento inicial e executa, "coda"; o analista de teste que verifica, testa e documenta; o cliente fica responsável em homologar, porém não quer se deparar com constantes pequenos erros; já o gerente de projetos, monitora o andamento do produto como um todo e é a ponte entre equipe técnica e cliente. Ele cobra, exige e dá o ritmo.

Todas essas funções são importantes. Mas, como evitar o aumento de pessoal voltado para atingir e manter a qualidade nas entregas de desenvolvimento? Como otimizar para que a equipe seja mais produtiva e mantenha a qualidade? Por que hoje é comum ver que o projeto vai ficando cada vez mais problemático, os orçamentos cada vez mais difíceis de acertar? Ou mesmo acertando o prazo. Por que as entregas sempre quebram outras funcionalidades?

Para responder essas perguntas é importante ressaltar que um modelo de trabalho horizontal se faz necessário. Aqui na ateliware, por exemplo, design e desenvolvimento andam lado a lado durante toda a construção de um software e o próprio desenvolvedor é o responsável pela realização dos testes, devido sua grande importância.

Vamos,  agora, entender o que é teste unitário automatizado e desenvolvimento orientado por comportamento (BDD - behavior driven development). A partir disso, vamos descrever um exemplo de aplicação dessas técnicas e qual foi o resultado, justificando, assim, porque a ateliware as utiliza em todos os projetos.

Teste unitário automatizado

Existem algumas divergências sobre a definição do que é teste unitário. Consideramos como o melhor entendimento a definição do Roy Osherove:

"Um teste unitário é um pedaço de código automatizado que executa uma funcionalidade unitária no sistema e depois faz uma conferência singular sobre uma hipótese no comportamento daquela determinada funcionalidade unitária".

Como foi descrito, o teste unitário é um pedaço de código automatizado, isto é, um programa executável que testa outro programa. Ele simula um acesso ou uma rota, podendo simular uma função interna de cálculo ou processamento, assim como acessos de telas.

O teste unitário automatizado poderá ser executado repetidas vezes para conferir se o comportamento esperado continua sendo o mesmo após alterações no código do sistema.

Cada teste unitário cobre uma parcela de código. Quando se depura uma execução é possível ver que a mesma percorre um caminho dentro dos códigos do sistema. Então, para cobrirmos todos os caminhos dentro do sistema, são criados vários testes unitários verificando se o resultado foi correto e também se a função retorna erros.

Digamos que queremos testar uma tela de login. Criamos um teste para avaliar se, quando o usuário informa os dados corretos, o login é realizado. Também criamos outro teste do oposto: informamos os dados incorretos e vemos se não logou. Se a aplicação estiver com um problema, pode permitir que o usuário logue mesmo fornecendo informações incorretas - e isso é preocupante. Portanto, a ideia é testar se o sistema dá certo quando tem que dar certo e se dá errado quando realmente precisa dar errado.

Dessa forma, geramos um grupo de testes e uma estatística do código coberto por eles. 90% do código deve possuir cobertura. Atualmente essa é a métrica mínima considerada, pois todo projeto apresenta alguns arquivos padrões do framework que abaixam a estatística mas não é necessário testá-los.

Então, conforme o código do sistema evolui, novos testes são criados para testar as novas regras implementadas. Não podemos entregar novas funcionalidades sem seus respectivos testes, pois isso baixa o que chamamos de percentual de cobertura de testes.

A cada entrega executamos o conjunto completo de testes e conferimos se todos passaram em suas checagens, e se a cobertura de testes continua no percentual aceitável.

Benefícios do teste unitário

O primeiro grande benefício é que fazemos com que os próprios desenvolvedores testem o que estão produzindo. Ao criar os testes unitários, eles acabam avaliando o código, sentindo coisas como performance, regras de negócio, etc.\ Outro benefício é a documentação. O conteúdo dos testes é um registro, que diz exatamente como o sistema deve se comportar e quando o sistema deve retornar erro. Desse modo, fica muito mais fácil incluir pessoas novas no projeto. Elas já recebem um direcionamento através dos testes.

Se a suíte de testes é executada sempre por completo em cada proposta de entrega, a própria aplicação automaticamente fará um teste de regressão bem completo, e vai apontar quebras em funcionalidades já desenvolvidas.

Muitas vezes nos deparamos com solicitações de alterações e melhorias onde se avalia o código e chega-se à conclusão de que o sistema deve ser massivamente alterado. O primeiro sentimento do desenvolvedor é o medo: medo de alterar códigos que ele não tem um conhecimento profundo da finalidade. Ou porque ele nem lembra mais do que ele fez alguns meses atrás, ou porque não foi ele quem produziu.

Então, fica extremamente mais fácil refatorar o projeto quando se tem uma boa suíte de testes montada e rodando. O desenvolvedor vai poder fazer alterações e rodar os testes que vão indicar exatamente onde ainda precisa ajustar para restaurar algo que tenha sido involuntariamente quebrado.

Desenvolvimento orientado por comportamento (BDD - behavior driven development)

A técnica de BDD foi criada, em 2006, por Dan North, com o objetivo de concretizar da maneira mais simples e clara possível o requisito solicitado pelo usuário.

Muitas vezes, mesmo documentando uma descrição do requisito havia uma dificuldade de entender qual comportamento o sistema deveria apresentar para que as requisições fossem atendidas. Mesmo após a documentação da solicitação haviam dúvidas pela equipe de desenvolvimento na hora de realmente implementar a mudança.

Era necessário criar uma união entre a equipe de desenvolvimento técnica e a equipe de negócio e o cliente. Algo que trafegue entre os 2 mundos, mas fixe uma definição coerente e não deixe dúvidas para ninguém.

Por esse motivo foi criada essa técnica, que visa levar o cliente que fez a solicitação até o ponto de se definir quais comportamentos e resultados o sistema deve apresentar para atender o que é desejado.

A idéia é definir junto com o cliente por 2 motivos: o primeiro é conseguir seu consentimento concreto sobre o assunto, o segundo é fazer essa documentação para que depois não haja dúvidas do que foi definido.

Para fazer uma definição de requisito deve-se seguir esse template:

  1. Given - Significa qual contexto inicial deve estar aplicado para se iniciar o teste do requisito. Quais cadastros já devem ter sido feitos, em qual tela o usuário já deve estar, quais parâmetros devem estar pré configurados, etc;

  2. When - Qual evento deve acontecer. Exemplos: digitação do teclado, leave de um campo, click de um botão, etc;

  3. Then - Qual resultado do evento deve acontecer, qual mensagem deve aparecer na tela, ou para onde deve redirecionar, etc;

  4. And - Quando é necessário criar mais de um evento ou resultado em sequência.

Existem casos de vários testes (thens) em uma mesma sequência de requisito, por exemplo:

Given - User logged on inicial dashboard And - There is a customer named "Default customer" on the database When - Clicks on customer link Then - Redirects to customer maintenance When - Selects customer named "Default customer" Then - Opens the "Default customer" details showing field name and other several fields When - Clicks on edit Then - All the fields transform on editable fields When - Changes the customer name field to "test" And - Clicks on update Then - All the fields become non editable And - The field name is changed to "test"

E assim vamos definindo todos os resultados dos requisitos solicitados pelo cliente, onde ele tem a possibilidade de criar em conjunto, validando se estão completos e se vão gerar o resultado esperado.

Quando o desenvolvimento é concluído, os desenvolvedores e o cliente podem usar essa sequência de comportamentos para testar o sistema e verificar se o desenvolvimento os está atendendo.

Esse guia de comportamentos é entregue para o cliente seguir e se torna uma referência do que ele tem que validar na entrega. Isso gera um desafio ao cliente, que os define da forma mais detalhada e fiel possível à necessidade.

A métrica sobre esses requisitos é que quando as entregas são realizadas, ao testá-los, nenhuma divergência pode ser encontrada.

Benefícios do desenvolvimento orientado por comportamento

O principal benefício é que essa técnica obriga o cliente e o desenvolvedor a pensarem de forma concreta e visual para definir o requisito; e a tentar extrair o maior número de detalhes possíveis para, no final, documentar a solicitação.

É um contrato quase assinado entre o cliente e o desenvolvedor sobre o que está sendo acordado, o que deve ser feito e como o sistema deve estar no final.

A aplicação adequada dessa técnica acaba com as suposições sobre o que deveria ser e minimiza as falhas de comunicação.

Conclusão e resultados

Após meses de desenvolvimento sem essas técnicas em um projeto, vemos que os desenvolvedores se cansam com a exaustão de desenvolver, quebrar coisas e fazer entregas com problemas. Além disso, os custos de um novo recurso se elevam, pois temos que testar tudo o que é desenvolvido e o que já foi feito em regressão. Vemos, também, a insatisfação do cliente à medida que a qualidade vai sendo prejudicada.

Os desabafos frequentes são: "foi feito o que eu não pedi", "no início as entregas eram mais rápidas", "apenas o desenvolvedor inicial do projeto entregava bem os desenvolvimentos", ou ainda, o cliente é quem deve homologar todo o sistema desde o início toda vez que uma entrega é realizada (já que as entregas sempre vem com erros grosseiros).

Ao aplicar as técnicas de testes unitários e desenvolvimento orientado por comportamento, na ateliware vemos de imediato um crescimento exponencial na confiança o time de tecnologia e o cliente. O relacionamento se fortalece ao longo do projeto. As entregas não são tão rápidas no início, pois a equipe está desenvolvendo as funcionalidades e ainda está desenvolvendo os testes, mas o ritmo de entregas não diminui, é constante, o lead time varia pouco e se torna uma base segura para novos orçamentos de novas fases. Além disso, as definições dos requisitos são bem documentadas e entregues conforme a expectativa do cliente.

Com isso, o desenvolvedor tem a segurança de alterar a aplicação e fazer uma entrega sem quebrar funcionalidades já construídas. Há uma facilidade maior em colocar desenvolvedores novos no projeto, os trabalhos não ficam mais lentos à medida que o projeto vai crescendo. O cliente entende melhor o que será desenvolvido através de sua solicitação, e os orçamentos são melhor estimados gerando menor variação, pois vários problemas de ambiente, desconhecimento do sistema e outras surpresas são evitadas.

Referências
Unit Testing, TDD, and BDD, Oh My. Your Guide to Different Unit Testing Techniques
What is automated testing?
Definition of a unit test
BDD como metodologia ágil
Manoel Souza
Manoel Souza

Software Engineer | Possui 16 anos de experiência com sistemas ERP, é App Full Stack developer e co-fundador da ateliware. Gosta de tocar violão e sempre que pode empresta seu talento musical para o grupo da igreja.

LinkedIn