TDD (Desenvolvimento Guiado por Testes)
A primeira pessoa a apresentar este método de desenvolvimento foi Kent Beck, autor do livro Extreme Programming, e relata em um capitulo de seu livro a necessidade de testar prematuramente, frequentemente e automaticamente, destacando que tal abordagem é necessária, pois quanto mais cedo encontrarmos erros mais barato será consertá-lo. Três anos mais tarde ele lança o livro Test Driven Development by example definindo uma estratégia de desenvolvimento que além de permitir detectar erros prematuramente, cria um ambiente de trabalho que favorece o desenho de uma boa arquitetura.
A principal característica do TDD é sua simplicidade, onde a principal regra a seguir é: criar um teste que falhe, escrever o código para o teste passar e em seguida refatorar seu código.
Perceba que não foi dito que uma linguagem é preferível que outras, nem que uma IDE é necessária para trabalhar desta forma. É claro que será necessário um framework de testes e o Kent Beck criou um que foi base para todos os outros que surgiram, o JUnit.
Certo, neste ponto começam a surgir as dúvidas, sobre como definir os métodos de testes, qual a granularidade do meu teste e por onde começar.
Um sistema sempre vai começar a partir dos requisitos ou estórias, uma boa documentação que defina quais serão os comportamentos do software.
Nós, na Sigma, descrevemos o comportamento de nosso sistema utilizando estórias. Então vamos escrever uma estória hipotética que define o comportamento de um sistema.
Eu como comprador
quero adicionar produtos comprados ao estoque
para manter o saldo em estoque atualizado;
Analisando a estória acima podemos extrair algumas entidades que o sistema de almoxarifado deve ter: Produto e Estoque. Além disso podemos visualizar os comportamentos esperados: dar entrada no estoque de um produto, consultar saldo de um produto em estoque;
Então podemos criar um teste que falhe para um dos comportamentos:
import unittest
class TestEstoque(unittest.TestCase):
def test_estoque_deve_dar_entrada_em_produto(self):
produto = Produto()
Este teste vai falhar, pois não existe uma classe Produto definida em nosso sistema.
import unittest
class Produto(object):
def __init__(self):
pass
class TestEstoque(unittest.TestCase):
def test_estoque_deve_dar_entrada_em_produto(self):
produto = Produto()
O código vai passar então podemos refatorar e criar corretamente um módulo e retirar o código da classe produto do module de teste;
Damos o segundo passo na implementação:
import unittest
class TestEstoque(unittest.TestCase):
def test_estoque_deve_dar_entrada_em_produto(self):
produto = Produto()
estoque = Estoque()
quantidade = 10
estoque.entrada(produto, quantidade)
Perceba que avançamos alguns passos extras na implementação, pois temos experiência na criação de classes e podemos ser flexíveis e escrever mais trechos de código que temos fluência. Esta flexibilidade não pode ser usada em excesso, pois o principal objetivo ao escrever código que falhe é que esta falha seja fácil de ser encontrada, portanto se eu escrever muitas linhas de código eu posso gastar muito tempo consertando ele. Portanto aprecie com moderação.
O código acima está falhando, pois o método entrada não está implementado.
Portando vamos implementá-lo:
class Estoque(object):
def __init__(self):
self._estoque = []
def entrada(self, produto, quantidade):
self.estoque.append({
'produto': produto,
'quantidade': quantidade
})
Novamente o teste vai passar, mas como definir que este teste é válido?
Asserções!
Asserção é um método para validar se uma condição de nosso teste é válida e caso ela não seja, minha asserção não será válida e vai lançar uma exceção.
import unittest
class TestEstoque(unittest.TestCase):
def test_estoque_deve_dar_entrada_em_produto(self):
# Definições
produto = Produto()
estoque = Estoque()
quantidade = 10
# Ações
estoque.baixar(produto, quantidade)
# Asserção
saldo = estoque.saldo(produto)
self.assertTrue(saldo == 10)
Perceba a estrutura de um testes, eu defino o estado inicial para meu teste, tomo uma ação e verifico se meu teste chegou ao estado esperado.
Este artigo serviu de base para o screencast sobre TDD, que foi gravado e está disponível neste link, com a apresentação também disponível neste link.