Este documento discute como os princípios de programação orientada a objetos tornam os testes guiados por testes (TDD) mais fáceis. Ele explica o mantra vermelho-verde-refatoração para TDD e princípios como responsabilidade única, inversão de dependência, não repetição do código e outros para produzir código testável e de alta qualidade.
TDD foi criado por Kent Beck, autor da conhecida metodologia ágil XP. TDD é um elemento essencial do XP.
Basicamente, em TDD nos modificamos um pouco a ordem do desenvolvimento.
Escreva um teste com o comportamento que vc imagina que será atendido. Ex.: calcular o total de horas de um período
Implemente o código para que ele teste passe o mais rápido possível. Pode cometer pecados nessa parte...
Refatore o código, remove duplicações e os pecados cometidos na fase anterior.
Normalmente, programar com TDD não é difícil. Porém requer disciplina. O problema é que estamos acostumados a partir direto para a solução sem pensar muito nas decisões que tomamos enquanto implementamos. Para fazer TDD corretamente, precisamos de alguns conhecimentos que vão nos ajudar escrever um código mais “testável”.
Um código “testável” tem: 1 - responsabilidades bem definidas -> é fácil entender o objetivo da classe 2 – flexibilidade -> é possível isolar as unidades testadas substituindo dependências complexas Veremos alguns conceitos podem nos ajudar a alcançar um código assim...
Recorrer a princípios básicos da orientação a objetos pode nos ajudar a criar um código mais testável.
Princípio da responsabilidade única O que seria responsabilidade? -> muito genérico Toda classe deve ter um, e somente um, motivo para ser modificada Exemplo...
FolhaDePonto precisa calcular a carga horária do mês ...
Só que também está calculando a carga horária do dia...
FolhaDePonto tem duas razões para ser modificada -> quebra do SRP -> carga horária do mês -> carga horária do dia
Devido ao alto nível de acoplamento (o calculo está sendo feito internamente), não temos como testar de forma eficiente o cálculo da carga horária
Vamos aplicar o princípio SRP e separar as responsabilidades que encontramos -> Dia e Mês
A classe dia é responsável por calcular a carga horária do dia
A classe mês é responsável por calcular a carga horária do mês
Agora podemos testar o cálculo da carga horária do dia de forma simples
Princípio da Inversão de Dependência -> Um módulo de alto nível não deve depender de um módulo de baixo nível, ambos devem depender de abstrações. Exemplo...
O sistema de controle de pagamento deve validar uma compra se o cartão for valido. Ele depende de uma operadora de cartão para aprovar o pagamento.
Ele depende de uma operadora de cartão para aprovar o pagamento e realizar a transação.
A classe da Operadora de cartões se comunica com um servidor específico para realizar as transações de compras.
O módulo ControleDePagamento depende diretamente de uma módulo de baixo nível (OperadoraVisa) -> quebra do DIP
Problema -> Devido a este forte acoplamento, todos os testes irão se comunicar com o servidor e realizar transações reais!! ..Imagine o prejuízo...
Solução -> ao invés de dependermos de uma representação específica da operadora de cartões, vamos criar uma dependência de uma abstração da operadora de cartões e recebê-la por parâmetro.
Removemos a dependência para a classe...
... e recebemos uma instancia da abstração por parâmetro no construtor
Agora o código está mais flexível, dessa forma podemos substituir uma classe de operadora de cartão real por uma classe falsa para o ambiente de testes.
Criamos uma classe falsa, que se comporte como uma operadora mas que ñ realize operações reais. -> Está de acordo com a abstração da operadora de cartões de crédito.
Agora podemos testar sem correr o risco de ter um prejuízo de R$ 1000 toda vez que o teste for executado...
Outros princípios que podem nos ajudar a escrever um código mais testável DRY -> Don´t Repeat Your Self -> não se repita: remova duplicações do código YAGNI -> You Gonna need it -> não acrescente complexidade desnecessária Lei de Demeter -> evite os “vagões de trem” nos código -> carro.motor.iniciar_rotacao => carro.ligar