Testes Unitários: Uma Abordagem Simplista e Agnóstica à Linguagem

“Sem problemas”, via Giphy

Atualmente, o teste de unidade é uma habilidade necessária para um engenheiro de software. Mas muitos deles não sabem disso e pensam que é algo de outro mundo.

O fato é que:

Testes de unidade são apenas algumas linhas de código escritas para garantir que outras linhas de códigos estejam funcionando conforme o esperado.

Essas linhas de código testadas são os recursos do seu aplicativo. É isso, não há nenhum tipo de mágica.

Eu não quero assustar ninguém. Então, não vou usar bibliotecas, padrões, classes e palavras-chave. Vou tentar explicar o teste unitário de maneira simplista. Usando código puro, sem qualquer importação de classes ou bibliotecas. Os exemplos estão em Python.

Motivação

Por que nós temos que fazer testes de unidade? Os testes unitários são a maneira mais simples de evitar ou alertar você sobre problemas. Como quando alguma alteração de código quebra outra funcionalidade do aplicativo.

Vamos ver um código por exemplo:

Como esperávamos, a função get_company_as_string funciona bem. Aqui estão alguns exemplos de alguns valores e os respectivos retornos:

 Em [1]: get_company_as_string ('Samsung') 
Out [1]: 'Nome: Samsung | Fundadores: Lee Byung-chul.
 Em [2]: get_company_as_string ('Apple Inc.') 
Out [2]: 'Nome: Apple Inc. | Fundadores: Steve Jobs, Steve Wozniak e Ronald Wayne.
 Em [3]: get_company_as_string ('Microsoft') 
Out [3]: 'Nome: Microsoft | Fundadores: Bill Gates, Paul Allen.
 Em [4]: ??get_company_as_string ('XPTO') 
 Em [5]: 

Mas, se precisarmos criar uma função que retorne a soma das idades dos fundadores na fundação da empresa? Então, precisamos ajustar a estrutura de dados para que a lista de fundadores mantenha sua idade.

Depois disso, temos o seguinte código. Essa é a base do código anterior, com uma modificação na estrutura dos fundadores e uma nova função:

E quando executamos nossa nova função, temos os seguintes resultados:

 Em [1]: get_sum_ages_of_company_founders ('Samsung') 
Para fora [1]: 26
 Em [2]: get_sum_ages_of_company_founders ('Apple Inc.') 
Saída [3]: 87
 Em [3]: get_sum_ages_of_company_founders ('Microsoft') 
Saída [3]: 41
 Em [4]: ??get_sum_ages_of_company_founders ('XPTO') 
 Em [5]: 

Assim, a implementação do novo recurso foi um sucesso e podemos implantar uma nova versão do nosso aplicativo. É isso? Não não não!

Vamos executar a função get_company_as_string . Observe que não alteramos essa função e já verificamos que ela estava funcionando bem. Vamos ver se continua indo bem:

 Em [6]: get_company_as_string ('Apple Inc.') 
Out [6]: "Nome: Apple Inc. | Fundadores: [{'name': 'Steve Jobs', 'age': 21}, {'name': 'Steve Wozniak', 'age': 25}, { 'nome': 'Ronald Wayne', 'idade': 41}]. "

Surpresa! Os resultados não são os mesmos e a função não funciona mais como esperávamos.

Sim, é um exemplo simples, mas imagine se nosso aplicativo tivesse mais de 100 ou 1000 funções. Como podemos garantir que todas as funções continuem funcionando como esperamos? Ou receber um alerta quando algo der errado?

Você sabe a resposta: testes unitários!

Asseverar / Esperar

Essa é a base e, provavelmente, a coisa mais simples do teste de unidade. É uma espécie de conversa com a função. Dizemos algo à função e verificamos se a resposta é o que deveria ser.

Por exemplo, como podemos testar uma função que some dois números? Dê à função dois números. Compare o retorno com a soma desses dois números, que já sabemos. Se a função estiver errada, a comparação falhará.

Sabemos que 2 + 2 é 4, -3 + -2 é -5 e -1 + 3 é 2. Então, se enviarmos 2 e 2 para a função, esperamos receber 4. Se enviarmos -3 e -2 , esperamos receber -5 e, se enviarmos -1 e 3, esperamos receber 2. Se um deles é diferente do que esperamos, a função está errada. É simples, certo?

Vamos ver como o teste unitário poderia ter nos ajudado em nossa refatoração anterior. Lembrando: não estou usando nenhuma biblioteca ou classe específica. Eu quero simplificar a explicação e mantê-la agnóstica.

Na versão antiga do código do aplicativo, a função get_company_as_string estava funcionando bem. Neste momento nós sabíamos algumas coisas:

  • Se dissermos 'Apple Inc.' para a função, esperamos receber 'Nome: Apple Inc. | Fundadores: Steve Jobs, Steve Wozniak e Ronald Wayne. ', E
  • Se dissermos 'XPTO' , esperamos receber Nenhum .

Então, no modo de codificação:

  • get_company_as_string ('Apple Inc.') deve ser igual a 'Nome: Apple Inc. | Fundadores: Steve Jobs, Steve Wozniak e Ronald Wayne. e
  • get_company_as_string ('XPTO') deve ser igual a None .

Aqui está o código:

Exemplo de teste minimalista sem classes e bibliotecas

… E a execução de testes:

 Em 1]: 
...: se get_company_as_string ('Apple Inc.') == 'Nome: Apple Inc. | Fundadores: Steve Jobs, Steve Wozniak
...: e Ronald Wayne. ':
...: print ('Sucesso no teste # 1')
...: outro:
...: print ('Erro no teste # 1')
...:
...: se get_company_as_string ('XPTO') for None:
...: print ('Sucesso no teste # 2')
...: outro:
...: print ('Erro no teste # 2')
...:
Sucesso no teste # 1.
Sucesso no teste # 2.

Agora, temos o código que garante o funcionamento correto da função. Então, podemos testar a funcionalidade após a implementação de um novo recurso.

 Em 1]: 
...: se get_company_as_string ('Apple Inc.') == 'Nome: Apple Inc. | Fundadores: Steve Jobs, Steve Woznia
...: k e Ronald Wayne. ':
...: print ('Sucesso no teste # 1')
...: outro:
...: print ('Erro no teste # 1')
...:
...: se get_company_as_string ('XPTO') for None:
...: print ('Sucesso no teste # 2')
...: outro:
...: print ('Erro no teste # 2')
...:
Erro no teste 1.
Sucesso no teste # 2.

Nós temos um erro. Ou isso seria um sucesso? As mudanças que fizemos quando implementamos o novo recurso quebraram a função get_company_as_string. Mas nossos testes podem identificar esse problema e agora podemos consertar isso.

Assim, com uma pequena mudança na função, podemos verificar que todos os testes foram aprovados e garantir a qualidade do nosso software:

Função get_company_as_string refatorada para funcionar após mudanças na estrutura de dados dos fundadores das empresas.