Skip to content
Analistas de testes estudando sobre cultura de testes no Blog DB1

Testar é preciso – Cultura de testes sobre a mesa

Salve leitor!

Seja bem-vindo a este texto que é o primeiro de uma série sobre testes de software. Para falar bem a verdade, a ideia é mais seguir pelo caminho da cultura de testes passando por várias partes do software, desde antes do código até a entrega final.

Esse primeiro texto não é um texto técnico. Você não vai aprender (agora) como escrever código de teste ou sobre frameworks e ferramentas. Aqui é para quem quer começar a pensar sobre testes, como favorecer o desenvolvimento de testes e que esses testes sejam eficientes.

Nem será tanto falado sobre testes automatizados neste momento, mas será dada uma introdução sobre os tipos de testes automatizados e como podem ajudar.

Então vamos à festa!

Desmistificando a cultura de testes

“Tá bom, então quer dizer que todos os desenvolvedores testam seu software e todos softwares são totalmente testados.”

Não é bem assim. Não é todo o software que é testado e não é todo teste que é executado que cumpre a função de um teste, que é a de garantir a qualidade do software.

Então, primeiro ponto: Para que serve um teste?

Existem alguns níveis de testes, vide a piramide de teste, e cada tipo de teste tem uma granulidade. Apesar disso um teste deve garantir que os mecanimos envolvidos na execução fazem o seu papel e os resultados esperados são atingidos.

Imagem para piramide de testes

Muito abstrato? Ok, deixe-me colocar de outra forma.

Imagine que você trabalha em uma fábrica de liquidificadores.

Liquidificador com botões

Quando um liquidificador sai da sua fábrica é esperado no mínimo que o liquidificador funcione, certo?

Então, antes de colocar o liquidificador na caixa, alguém vai pegar o liquidificador, por na tomada e apertar os botões dele para garantir que o liquidificador liga e muda a velocidade para mais rápido ou mais devagar de acordo com cada botão. Isso seria um teste de interface com end-to-end que garante que o produto final funciona de acordo com o que ele apresenta e se destina.

Mas e se neste ponto o liquidificador não ligar?

Primeiro, você não vai entregar o que é horrível. E segundo vem a pergunta: “O que está errado?”. Neste momento tem tanta coisa que pode estar errada e não é o cara que está testando que vai descobrir o que está errado. O que ele fará será devolver para o passo anterior, que chamaremos aqui de montador (perdão, não tenho tanto conhecimento sobre linha de produção de liquidificadores, é só uma analogia).

O montador antes de entregar o motor fechado deveria garantir que o botão aciona o mecanismo. O mecanismo ajusta a potência, além de ligar o motor, e o motor está funcionando para aquela potência. Se tudo estiver bem, ele fecha o motor e manda adiante. Isso seria um teste de integração que valida que todos os componentes envolvidos estão funcionando quando colocados juntos.

Você já deve estar pensando “é lindo, mas e se neste momento não funcionar?”. E também já deve ter chego na conclusão de que o montador irá diagnosticar qual dos componentes não está funcionando corretamente e repassará isso ao técnico responsável.

Digamos que o problema esteja no motor. O responsável técnico do motor irá abrir o motor, usar um amplo conjunto de ferramentas para diagnosticar cada parte do motor e consertar esse motor e assim colocar de volta para ser usando na montagem. Isso seria o que nós chamamos de teste unitário. É o que o desenvolvedor faz para garantir que o código funciona.

Os testes servem para garantir que o está feito nesta fase atende o que é necessário para a próxima fase.

É claro que aqui nesta analogia os testes são performados manualmente, porém, quando se trata de software todos os tipos de testes aqui ilustrados podem ser automatizados. Isso traz uma precisão maior nas reexecuções, porque a máquina executa sempre tudo do mesmo jeito.

Além disso, o tempo de execução é muito curto. Assim é possível com uma execução da suíte de testes saber o impacto de uma determinada alteração de forma muito breve e com qualidade sendo possível encontrar rapidamente possíveis problemas.

Está mais claro agora? Espero que sim. Sigamos!

Cobrindo as bases: Passo 1 – Entender a regra

Como vimos na analogia e na pirâmide, o primeiro de tudo é começar com os testes unitários que devem ser a maior quantidade de testes que sua aplicação deve ter.

O código é a forma de instruirmos uma máquina para executar uma regra de negócio, então tudo parte da regra de negócio. É a regra de negócio que irá dizer o que deve ser feito e o que o código deve fazer. Vamos fazer um ilustração disso.

Peguemos a seguinte regra:

“Para calcular o vencimento da fatura é preciso calcular o primeiro dia útil, 5 dias após o fechamento da fatura. Para considerar um dia útil é necessário que não seja sábado, domingo ou feriado. Caso o vencimento fique mais do que 7 dias após o fechamento, então o vencimento deverá ficar para o último dia útil possível antes de 5 dias.”

Se um desenvolvedor ler isso em um requisito ele irá saber exatamente o que fazer, porém garanto que o código ficará ligeiramente divergente da regra.

via GIPHY

“Quer dizer que o desenvolvedor não irá implementar essa regra corretamente?”

Ele vai implementar corretamente sim. O que quero dizer é que na programação teremos a regra escrita de forma que essa regra será quebrada em duas.

Siga meu raciocínio e observe:

“(1) Para calcular o vencimento da fatura é preciso calcular o primeiro dia útil 5 dias após o fechamento da fatura. (2) Para considerar um dia útil é necessário que não seja sábado, domingo ou feriado. (3) Caso o vencimento fique mais do que 7 dias após o fechamento então o vencimento deverá ficar para o último dia útil possível antes de 5 dias.”

Notemos que (1) e (3) estão relacionados ao cálculo do vencimento, já (2) está relacionado ao cálculo do dia útil. Leia então a regra novamente, escrita de forma ligeiramente diferente na ordem:

“Para calcular o vencimento da fatura é preciso calcular o primeiro dia útil 5 dias após o fechamento da fatura. Caso o vencimento fique mais do que 7 dias após o fechamento, então o vencimento deverá ficar para o último dia útil possível antes de 5 dias.

Para considerar um dia útil é necessário que não seja sabádo, domingo ou feriado. “

Note que agora a regra do cálculo do vencimento está agrupada em um parágrafo, e a do dia útil em outro.

“Agora está top! vai para os testes”

Caaaaaaalma. Agora vem alguns parágrafo densos, mas bem importantes.

Pode parecer inofensivo ou pouco relevante essa discussão sobre como a regra deve ser descrita. Você pode até estar se sentindo de volta às aulas do ginásio ou colégio (sim, na minha época eram esses os nomes) e isso acontece porque parte da regra ser tão importante é que ela precisa ser bem compreendida por quem irá escrever um código.

A regra especificada é a maneira que uma pessoa entendeu o que foi comunicado pelo cliente. Ela deve comunicar esse mesmo conhecimento ao desenvolvedor que irá comunicar através de código como a máquina deve fazer essa regra acontecer.

Muito comunicar/comunicação/comunicado, né? É intencional, porque podem existir sempre ruídos de comunicação, então em cada etapa pode ser adicionado um ruído que pode comprometer a sua entrega. #Risco

Em um mundo ideal no processo cliente -> analista -> desenvolvedor -> código, tudo será 1:1 (um-para-um). Ou seja, o que for passado irá para o código de produção.

Lógico que nem todo processo está no nosso controle e tem causas nos ruídos de comunicação, mas prometo falar disso em outro momento. Por ora vamos concordar em alinhar a linguagem entre requisito e código para reduzir esse ruído e escrevermos testes mais eficientes.

Nesse espírito de estarmos alinhados, vamos fazer uma última mudança na regra acima. Prometo!

Recado de "sorry", ou "desculpe"

Tem uma regra de Clean Code que norteia a boa qualidade dos códigos escritos. Ela foca em evitar condições negativas porque são difíceis de entender (falaremos sobre o efeito da negação em outro momento também). Esse princípio também pode ser aplicado na especificação, e com isso nossa regra fica:

“Para calcular o vencimento da fatura é preciso calcular o primeiro dia útil 5 dias após o fechamento da fatura. Caso o vencimento fique mais que 7 dias após o fechamento, então o vencimento deverá ficar para o último dia útil possível antes de 5 dias.

Para considerar um dia útil é necessário que seja de segunda a sexta e não seja feriado. “

Agora sim!

“Ah, mas você não tornou positiva, você só trocou o ‘não’ de lugar”

Parece mesmo meu afoito leitor, mas vejamos logicamente:

Antes -> dia não sábado E dia não domingo E dia não feriado

Agora -> dia entre segunda e sexta E dia não feriado

Antes você tinha que negar 3 vezes e juntar as negações, agora você tem uma condição bem natural e sequencial entre segunda e sexta e tem uma condição negativa que é apenas uma inversão de uma constatação. #PolemicaDetected

Enfim, com isso finalizamos o primeiro passo.

via GIPHY

Cobrindo as bases: Passo 2 – Exemplificando os casos da regra

“Mas não largou o osso da regra ainda”

Tá, desculpa, mas não larguei não. Como a regra é razão de estar escrevendo o código muita coisa vem daí. Então sigamos.

Como dito anteriormente, a regra acima é boa e clara o suficiente para que o desenvolvedor transforme isso é um código.

Mas tem como melhorar 🙂

“Ah é? Me dá um exemplo de como?”

Não vou te dar 1, vou dar vários.

via GIPHY

Uma forma de enriquecer o entendimento da regra é dando exemplos dela. Mas como escolher quais exemplos dar?

Para nos ajudar nisso, vamos recorrer à velha máxima de que uma “imagem vale mais do que mil palavras“. Vejamos essas duas então:

Fluxograma cálculo do vencimento
Fluxograma Cálculo do Próximo dia útil

Essas imagens são os fluxogramas referentes às regras. Basicamente ele mostra passo a passo como ele vai obter as informações e calcular o que é necessário até chegar nos resultados. Lindo, não é mesmo?

“E o que isso tem a ver com exemplificar a regra? Quer que eu faça esses desenhos agora?”

Seria muito útil e seria uma contingência no alinhamento, mas ok, não estou em posição de cobrar nada e sim de instruir.

Vamos usar isso então para montar os exemplos. Olhado para esses desenhos vamos estabelecer as seguintes regras:

  • Deve sempre ir do começo [icone:começo] até o fim [icone:fim];
  • Quando existe mais de 1 fim precisamos chegar em todos;
  • Quando existir um elemento de decisão, precisamos sair para ambos os lados;
  • Sempre devemos usar valores que façam sentido;

Simples! Só isso 🙂

Então vamos lá, mão na massa.

Exemplos do vencimento:

Exemplo 1 -> Dado fechamento da fatura em 09/06/2019 o vencimento será em 14/06/2019.

Exemplo 2 -> Dado fechamento da fatura em 10/06/2019 o vencimento será em 17/06/2019.

Exemplo 3 -> Dado fechamento da fatura em 11/06/2019 o vencimento será em 17/06/2019.

Exemplo 4 -> Dado fechamento da fatura em 12/06/2019 o vencimento será em 17/06/2019.

Exemplo 5 -> Dado fechamento da fatura em 13/06/2019 o vencimento será em 18/06/2019.

Exemplo 6 -> Dado fechamento da fatura em 02/09/2020 e considerando 07/09/2020 feriado o vencimento será em 08/09/2020.

Exemplo 7 -> Dado fechamento da fatura em 31/08/2020 e considerando 07/09/2020 feriado o vencimento será em 04/09/2020.

Exemplos do dia útil:

Exemplo 1 -> Dado dia 05/10/2019 sendo segunda e não feriado então é dia útil.

Exemplo 2 -> Dado dia 06/10/2019 sendo terça e não feriado então é dia útil.

Exemplo 3 -> Dado dia 07/10/2019 sendo quarta e não feriado então é dia útil.

Exemplo 4 -> Dado dia 08/10/2019 sendo quinta e não feriado então é dia útil.

Exemplo 5 -> Dado dia 09/10/2019 sendo sexta e não feriado então é dia útil.

Exemplo 6 -> Dado dia 10/10/2019 sendo SÁBADO então NÃO é dia útil.

Exemplo 7 -> Dado dia 11/10/2019 sendo DOMINGO então NÃO é dia útil.

Exemplo 8 -> Dado dia 12/10/2019 sendo segunda e feriado então  NÃO é dia útil.

Veja só que legal! Com esses 15 exemplos enxutos é possível passar por todos os caminhos possíveis nos fluxogramas e fica muito claro o que esperar da regra em cada caso. Ao fazer isso foi:

  • Explicitado valores e limites mensuráveis para eles;
  • Tratado a regra em todas suas nuances;
  • Repassado todos fluxo das regras esmiuçadamente;
  • Tirar a prova real das regras em relação ao que foi entendido, escrito e finalmente codificado;
  • Diminuido a distância 1:1 entre regra e código.

Neste momento um desenvolvedor habituado aos testes e perspicaz lendo isso deve ter dado um sorrisinho de canto de boca e pensado:

“Eu vi o que você fez aí: cada um desses exemplos dá para fazer um teste unitário”

via GIPHY

Conclusão

Sim, caríssimo leitor, cada um desses exemplos dados irá virar um teste unitário. Cada um servirá para, dado a especificação da regra, validar se ela está de acordo quando o desenvolvedor falar com o analista, e quando o desenvolvedor for escrever o teste. Também servirá para a homologação validar a regra em pelo menos esses casos.

Se o no fim das contas algo der errado, o desenvolvedor pode encontrar o cenário e entender a situação resolvendo um possível bug mais rapidamente. Ou até criar um caso de regressão que pode retroalimentar/retificar a especificação.

Isso aumenta a qualidade do trabalho em várias etapas do processo, diminui a probabilidade de erros e aumenta a eficiência na resolução dos casos que ocorrerem. #EntregaDeValor

Note que uma vez que os exemplos viram cenários, validar que a regra no código está correta é questão de milésimos de segundos. Bom, não é?

Acho que chega por hoje 🙂

Obrigado pelo seu precioso tempo 🙂

Na próxima irei falar tecnicamente como dados esses exemplo os testes unitários podem ser escritos e ficarem top! Aguardem.

Compartilhe:

This Post Has 0 Comments

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Back To Top