1.3.1 Exemplo Simples
Last updated
Last updated
Nesta seção, apresentamos um exemplo simples do ciclo TDD ilustrado na Seção 1.3, com base na especificação da sequência de Fibonacci, utilizando por Kent Beck (2003).
Considere a especificação do problema da sequência de Fibonacci apresentada a seguir:
A sequência de Fibonacci é constituída da seguinte forma: 1 1 2 3 5 8 13 21 34 55 e assim por diante. Nela, está presente o seguinte padrão. Um mais um igual a dois. Um mais dois é igual a três. Dois mais três são iguais a cinco. E assim por diante. Ou seja, o sucessor de um número na sequência é formada pela soma dos dois números anteriores a ele.
Todo número na série Fibonacci é de aproximadamente 0,618 a 1 em termos de sua relação com o número após ele. Essa proporção nunca muda porque a proporção permanece a mesma. O interessante da sequência é que ela gera a chamada espiral perfeita, conforme ilustrado na imagem abaixo. (Fonte: https://www.ambroker.com/pt/analysis/blog/sequencia-de-fibonacci/)
Desse modo, suponha que fossemos implementar uma classe com exemplos matemáticos e um desses exemplos fosse um método que gerasse a sequência de Fibonacci. O código completo do exemplo apresentado abaixo está disponível no GitHub no endereço https://github.com/aurimrv/fibonacci. Os arquivos foram numerados sequencialmente para facilitar a apresentação das mudanças implementadas nos testes e na aplicação em si.
Caso não queira instalar as ferramentas de desenvolvimento em seu computador, é possível acompanhar e/ou realizar a execução pelo CoLab, no link https://colab.research.google.com/drive/1BDFJ_FroYLZFxQB0hhwS1SYESIea5L-K?usp=sharing.
Ao aplicar TDD para essa construção, iniciaríamos a escrita de um caso de teste (tests01.py
) que representaria uma história de usuário conforme ilustrado abaixo:
O exemplo acima, ilustra o uso de um framework de execução de testes em Python denominado UnitTest. Precisamos dele para conduzir a execução automática de nossos testes. É importante observar que ele não é o único existente para tal finalidade. Temos muitos outros disponíveis para Python. Todo conjunto de teste em UnitTest deve ter nome iniciando com "test
", como "tests01.py
".
Vamos destrinchar o código acima. Inicialmente, na linha 1, indicamos que iremos fazer uso do unittest
. Em seguida, na linha 2, o teste indica que estamos assumindo a existência de um pacote math_samples01
que possui dentro dele uma classe denominada MathSamples
. Em seguida, na linha 4, é criada uma classe, denominada FibonacciTest
a qual estende a classe unittest.TestCase
do framework UnitTest. Na linha 6 é criado um método que representa um caso de teste. Métodos que representam casos de teste devem ter o nome iniciado com test_
pois é desse modo que o framework identifica quais métodos ele deve executar automaticamente. Finalmente, na linha 7, o método desejado é invocado com o valor 0
(MathSamples.fibonacci(0)
). Conforme a definição, por definição, o valor da sequência de Fibonacci para o valor de n=0
deveria ser 0
. Assim, o método self.assertEquals
do framework recebe dois parâmetros que permitem realizar uma comparação do valor obtido com a execução do método da aplicação com o valor esperado conforme a especificação.
A partir do relato acima, observa-se que o caso de teste está guiando a escrita da aplicação. Ou seja, para fazer esse teste passar, os pré-requisitos demandados devem ser implementados para que o interpretador Python não acuse erros de sintaxe por não encontrar os elementos do qual o teste depende. Assim sendo, antes de executar o teste acima, construímos o esqueleto da implementação da classe MathSamples
que, conforme pode ser observado no teste, tem o método estático fibonacci
que aceita um número como argumento. Essa classe poderia ser especificada conforme abaixo e salva no arquivo chamado de math_samples01.py
, salva no mesmo diretório do arquivo tests01.py
.
Para a execução dos testes, basta executar o comando abaixo:
Como era de se esperar, o teste falhou pois a classe em teste ainda não implementa a funcionalidade capaz de fazer o teste passar.
Em seguida, para passar o teste, iremos iniciar a escrita do código funcional da aplicação de modo a fazer o teste passar. Segundo Beck (2003), devemos perder aqui a menor quantidade de tempo possível para fazer o teste passar. Haverá o momento para melhorias. Por hora, a intenção é apenas a de fazer o teste passar com o menor esforço possível. A nova versão do método fibonacci
da classe MathSamples
, abaixo (arquivo math_samples02.py
), tem esse objetivo.
Observamos que nela, a única alteração foi a de remover o comando pass
e o substituir por um return 0
(linha 4). Desse modo, ao repetir a execução do teste (arquivo tests02.py
), ele irá passar, conforme apresentado a seguir.
Como o código do método fibonacci
da classe MathSamples
ainda é bem simples, não há necessidade de execução do passo 3 nesse momento e, desse modo, retorna-se ao 1º passo do ciclo para a escrita de mais testes.
O próximo teste, explora o segundo número da sequência de Fibonacci. Agora, nosso arquivo de teste, nomeado de tests03.py
, apresenta o conteúdo abaixo:
Ao executar os testes, o segundo (test_fib02
) irá falhar, conforme ilustrado abaixo. Ou seja, fibonacci(1)
, conforme nossa definição, é 1
e o código atual retorna 0
.
Escrevendo o código que faça o teste passar, a implementação do nosso método fibonacci
, fica como se segue (arquivo math_samples04.py
):
E agora, ao executar os testes (arquivo tests04.py
) para essa implementação, ambos os testes passam:
Como o código do método fibonacci
da classe MathSamples
ainda é bem simples, não há necessidade de execução do passo 3 nesse momento e, desse modo, retorna-se ao 1º passo do ciclo para a escrita de mais testes.
O próximo teste, explora o segundo número da sequência de Fibonacci. Agora, nosso arquivo de teste, nomeado de tests05.py
, apresenta o conteúdo abaixo:
Ao executar esse teste na implementação math_samples05.py
obtemos o resultado abaixo, ou seja, surpreendentemente, o nosso código responde adequadamente para esse novo teste, sem que seja necessário qualquer alteração pois, fibonacci(2)
é 1
, conforme nossa especificação e é assim que a aplicação se comporta:
Escrevemos, então, mais um teste, agora para o quarto número da sequência, ou seja, fibonacci(3)
, o qual deveria ser 2
(arquivo tests06.py
, abaixo):
Ao executar o teste, a aplicação não se comporta corretamente, conforme apresentado a seguir.
Escrevendo o código que faça o teste passar, a implementação do nosso método fibonacci
, fica como se segue (arquivo math_samples07.py
):
Após a correção, ao executar o conjunto de teste (arquivo tests07.py
), todos eles passam:
Nesse momento, é possível observar o código e eliminar redundância uma vez que, ao retornar 1 + 1
, têm-se que esses valores já eram retornados quando se calculou o fibonacci(2)
e fibonacci(1)
, ambos retornavam 1
.
Desse modo, o primeiro 1
do return
, poderia ser substituído por uma chamada recursiva a fibonacci(n-1)
(arquivo math_samples08.py
).
Em seguida, o segundo 1 pode ser substituído por fibonacci(n-2)
, conforme código abaixo (arquivo math_samples09.py
).
Após as melhorias, o conjunto de teste (arquivo tests09.py
) é executado com sucesso, encerrando o ciclo TDD. Obviamente, novos testes podem ser redigidos para verificar se continua funcional.