André Faria Gomes

Test Driven Development

Publicado por: andrefaria em: 12 Julho 2008

O contato com Scrum, XP e Agile em geral, me levaram à diversos conceitos que até então desconhecia, um dos quais resolvi me aprofundar  foi TDD (Test Driven Devolpment), para isso li o livro ”Test-Driven Development by Example” escrito por Kent Beck. O Livro é muito esclarecedor e divertido de ler, apresenta diversos conceitos e técnicas através de exemplos de implementações de pequenas soluções.
TDD ou Desenvolvimento Dirigido por Testes, é uma técnica de desenvolvimento de software que consiste em pequenas iterações onde testes são escritos primeiro e o código  produzido é somente o necessário para fazer o teste passar, e finalmente o código é refatorado para acomodar as mudanças. TDD proporciona feedback rápido depois de cada mudança. Não é considerado somente  uma ténica de escrita de testes, mas uma técnica para design de software.

TDD pode ser facilmente explicado em cinco simples passos:

  1. Adicione um teste rapidamente.
  2. Execute todos os testes e observe o novo teste falhar.
  3. Faça uma pequena mudança para fazer o teste passar .
  4. Execute todos os teste e observe que foram bem suscedidos.
  5. Refatore e remova o código duplicado.

Para melhor entender este ciclo de cinco fases, vamos constuir um pequeno programa Java para realizar um simples cálculo de potência. Para executar os testes precisamos de uma ferramenta de testes unitários, utilizaremos o JUnit.
O JUnit possui uma classes chamada TestCase, a qual nossa classe de teste deverá extender que possui uma série de métodos implementados que nos auxiliaram no processo de testes, estes métodos fazem o teste falhar caso suas condições não sejam satisfeitas.

Ex: O método “AssertEquals(x,y)“  faz o teste falhar caso x seja direferente de y.

assertEquals(“TDD”,”DDD”); //Falha
assertEquals(100, 100); //Passa

Começaremos então pelo teste:

public class CalculadoraTest extends TestCase {
public void testCalcular(){
Calculadora calculadora = new Calculadora();
assertEquals(calculadora.calcularPotencia(8, 2), new BigDecimal(“64”));
assertEquals(calculadora.calcularPotencia(2,10), new BigDecimal(“1024”));
assertEquals(calculadora.calcularPotencia(3, 3), new BigDecimal(“27”));
}
}

Se tentarmos executar nosso teste, o JUnit nos apresentará uma barra vemelha o que signfica que o teste falhou, e falhou porque a classe Calculadora e o método estático calcularPotencia ainda não existem.

Nossa missão agora é fazer, rapidamente, o teste passar. Para isso vamos criar a classes Calculadora e o método calcularPotencia.

public class Calculadora {
public void calcularPotencia(Integer a, Integer b){
return new BigDecimal(“64”);
}
}

Se executarmos o teste novamente o mesmo passará… Eu seu, eu sei, que isso pode parecer meio estranho no inicio, mas não se assuste vai se acostumar e entender melhor o que está por traz disso tudo com o tempo. Agora que o teste passou temos que realizar o processo de refatoração para remover as constantes e a redundancia do código, assegurando sempre que o teste continue passando.

public void calcularPotencia(Integer a, Integer b){
BigDecimal result = new BigDecimal(a);
for (int I = 0; I < b; I++) {
result = result.multipliedBy(BigDecimal.valueOf(a));
}
return result;
}

Pronto! Refatoramos nosso código removendo as constantes e se executarmos nossos teste verificaremos que este será bem sucedido! Este é o processo básico de TDD.

Ops!!! O que acontecerá se tentarmos executar o método executar uma potencia de um número elevado a zero? Problemas!!! Todo número elevado a Zero é igual a 1 (Um), dessa forma devemos adicionar um assert que verifique um número elevado a zero retorna 1, fazer o teste passar, refatorar e assegurar de que o teste continua passando.


public void calcularPortencia(Integer a, Integer b){
if (b.equals(0)){
return BigDecimal.valueOf(1);
}
BigDecimal result = new BigDecimal(a);
for (int I = 0; I < b; I++) {
result = result.multipliedBy(BigDecimal.valueOf(a));
}
return result;
}

public void testCalcular(){
Calculadora calculadora = new Calculadora();
assertEquals(calculadora.calcularPortencia(8, 2), new BigDecimal(“64”));
assertEquals(calculadora.calcularPortencia(2,10), new BigDecimal(“1024”));
assertEquals(calculadora.calcularPortencia(3, 3), new BigDecimal(“27”));
assertEquals(calculadora.calcularPortencia(21, 0), new BigDecimal(“1”));
assertEquals(calculadora.calcularPortencia(8, 0), new BigDecimal(“1”));
}

Espero ter contribuido de alguma forma para que você compreende-se melhor o processo de TDD, estou a disposição para esclarecimento de qualquer dúvida, e os comentários são muito bem vindos.

3 Respostas para "Test Driven Development"

Parabéns pelo artigo André. A leitura é fácil.

Fala André!

Minha primeira visita aqui no seu blog!
Legal que está se aprofundando em TDD… um assunto que ninguem leva a serio!
Bom, para estrear, vou colocar algumas observações, ok?

1) Nos primeiros passos, antes de fazer o teste passar, é necessário fazer o teste falhar. Mas é importante não misturar ‘erro de compilação’ com ‘falha do teste’. O teste só poderá falhar, se, ao menos, o código for compilável. Para faze-lo falhar, basta retornar, por exemplo, BigDecimal.ZERO. O legal de fazer o teste falhar no inicio é que temos certeza de que o que estamos querendo testar está sendo testado (ex.: não estou fazendo ‘import’ de uma outra classe ‘Calculadora’ que já funciona), e, além disso, a ferramenta (ex.: JUnit) não está louca.

2) Logo depois que você fez o método ‘calcularPotencia()’ retornar um ‘new BigDecimal(“64”)’, você decidiu fazer refactoring… ótimo. Porém você acabou avançando demais e trocou a implementação ‘hardcode’ por uma solução melhor, *sem criar uma nova especificação de funcionalidade/behaviour por meio de testes*! É necessário ‘baby steps’, isto é, antes de você retirar o hardcode ‘64′, prove através de um outro teste que esta implementação está incompleta! Esse processo é chamado de ‘triangulation’. É é extremamente útil para descobrir (e cobrir) diversos casos de testes. Baby steps, sempre!

3) É interessante separar o exercício do SUT em diversos métodos de testes. Eu costumo quase sempre exercitar apenas uma vez um método do SUT por método de teste. No seu exemplo, eu colocaria o ‘teste com expoente zero’ em um método separado e daria um nome de acordo com o ‘Testcase Class per Class’ (xUnit), tornando explícito a feature (método do SUT) e a fixture… algo como ‘testCalculaPotencia_ComExpoenteZero’. Mais pra frente, se surgir a necessidade, poderia refatorar o teste para usar o ‘TestCase Class per Fixture’, e o nome do método ficaria ‘expoenteZero_deveRetornarUm’.

Espero ter contribuido um pouco com o assunto!

Muito bom Gerson, concordo completamente, seu comentário enriqueceu muito este post… Grande abraço!

Deixe um comentário

Anuncios

Add to Technorati Favorites

André’s Twitter