You are on page 1of 22

Test Driven Development: TDD

simples e prtico
Veja nesse artigo uma forma simples e prtica o
Desenvolvimento Orientado a Testes (TDD) e suas vantagens
em relao aos mtodos de desenvolvimento tradicionais.
0

Gostei (3)
(0)
Fala desenvolvedores!!!
Hoje falarei um pouco sobre TDD, mas sem aprofundar no tema, apenas para plantar
a sementinha da idia. Este o primeiro de uma srie de artigos que tero uma
anlise mais profunda onde resolveremos alguns problemas com TDD.

Definio simples
TDD Alexandre? O que ? TDD o Desenvolvimento Orientado por Testes (Test
Driven Development). Isso mesmo! Desenvolvemos o nosso software baseado em
testes que so escritos antes do nosso cdigo de produo!
Se voc nunca ouviu sobre TDD, ou j ouviu mas nunca tentou, sugiro ferozmente
que voc continue lendo o artigo e procure sobre o assunto! A idia do TDD j
antiga e foi firmada com o mestre Kent Beck (Autor tambm do famoso livro sobre
TDD, que recomendo) e um dos pilares (l-se prticas) do Extreme Programming!
Basicamente o TDD se baseia em pequenos ciclos de repeties, onde para cada
funcionalidade do sistema um teste criado antes. Este novo teste criado inicialmente
falha, j que ainda no temos a implementao da funcionalidade em questo e, em
seguida, implementamos a funcionalidade para fazer o teste passar! Simples assim!

Calma! No to rpido pequeno samurai! No podemos simplesmente escrever outro


teste s por que j temos um teste passando. preciso que esta funcionalidade que
acabamos de escrever seja refatorada, ou seja, ela precisa passar por um pequeno
banho de "boas prticas de Desenvolvimento de Software. Estas boas prticas que
garantiro um software com cdigo mais limpo, coeso e menos acoplado.

Ciclo de desenvolvimento
Red, Green, Refactor. Ou seja:

Escrevemos um Teste que inicialmente no passa (Red)

Adicionamos uma nova funcionalidade do sistema

Fazemos o Teste passar (Green)

Refatoramos o cdigo da nova funcionalidade (Refactoring)

Escrevemos o prximo Teste

Figura 1: Ciclo de desenvolvimento do TDD


Ns temos, neste tipo de estratgia, um feedback rpido sobre a nova funcionalidade
e sobre uma possvel quebra de outra funcionalidade do sistema. Assim tempos muito
mais segurana para as refatoraes e muito mais segurana na adio de novas
funcionalidades.

Motivos para o uso

Temos diversos ganhos com esta estratgia e vou citar alguns:

Feedback rpido sobre a nova funcionalidade e sobre as outras funcionalidades


existentes no sistema

Cdigo mais limpo, j que escrevemos cdigos simples para o teste passar

Segurana no Refactoring pois podemos ver o que estamos ou no afetando

Segurana na correo de bugs

Maior produtividade j que o desenvolvedor encontra menos bugs e no


desperdia tempo com depuradores

Cdigo da aplicao mais flexvel, j que para escrever testes temos que
separar em pequenos "pedaos" o nosso cdigo, para que sejam testveis, ou
seja, nosso cdigo estar menos acoplado.

Mito
Muitos ainda no gostam da idia do TDD pelo fato de termos mais cdigo a ser
desenvolvido, acarretando maior tempo no desenvolvimento de uma funcionalidade.
Mas isto est errado. Com toda certeza voc desenvolvedor j corrigiu um bug no
sistema, mas criou outros dois no lugar. Isto acontece com muita frequncia e muitas
das empresas ainda pagam os desenvolvedores somente para corrigirem bugs e at
reescreverem sistemas cuja manuteno terrvel, traumtica e sangrenta!

Reforando os motivos
A mdio prazo (e dependendo do sistema a curto prazo) este tempo de
desenvolvimento com TDD menor que o tempo de manuteno corrigindo bugs e
mesmo para adicionar funcionalidades novas. Isto devido, resumidamente, a:

Confiana do desenvolvedor na correo de bugs, pois qualquer passo errado


ser mostrado pelos testes

Tempo de desenvolvimento menor na adio de funcionalidades, j que o


sistema mais flexvel e o cdigo mais limpo

Menor tempo do desenvolvedor ao corrigir bugs aps aquelas incessantes brigas


com o pessoal de qualidade depois da famosa frase "Mas na minha mquina
funciona!"

Possibilidde de Integrao Contnua, com builds automticos e feedbacks


rpidos de problemas.

Sendo chato, parte I


Sei que chato, mas ainda falarei um pouco de teoria neste incio, para que
realmente possamos entender (leia-se lembrar!) sobre alguns conceitos e motivos do
uso de TDD.
Como havia dito, o TDD se baseia no ciclo visto na Figura 1:
Vamos entender um pouco sobre cada etapa:

Novo Teste
Este primeiro passo o pilar do TDD (no brinca!). Temos uma nova funcionalidade
do sistema e fazemos o processo inverso ao tradicional: Testamos e Codificamos e
no Codificamos e Testamos.
No primeiro momento isto parece estranho, esquisito ou feio, mas no . O fato de
termos um teste primeiro que o cdigo garante que daremos passos simples para a
codificao da funcionalidade, afim de fazer o teste passar, ou seja, seremos
"obrigados" a escrever uma implementao simples para que o teste passe.
No comeo esta forma no muito intuitiva e o grfico de aprendizagem no l um
dos melhores, mas com o tempo e aperfeioamento da tcnica, esta ser a forma
mais intuitiva e segura de desenvolver que voc encontrar.

Teste Falhando
Neste momento, acabamos de escrever o teste e no temos a implementao. bvio
que o teste falhar, pois ele espera uma resposta que ainda no temos implementada

em lugar algum. Com um Teste falhando na nossa frente, temos um nico objetivo na
vida: Faz-lo passar! Passamos para a prxima fase:

Nova funcionalidade
J ouviu falar no KISS? "Keep It Simple, Stupid", ou seja, devemos escrever o nosso
cdigo da forma mais simples possvel. Cdigo limpo, simples e funcional! Esta a
idia.
Assim, neste momento vamos esquecer as Boas prticas, a Inverso de Controle, os
Patterns, etc e vamos codificar a nossa nova funcionalidade da forma mais simples
possvel para fazer o nosso Teste passar. Neste momento estamos simplesmente
escrevendo alguma funcionalidade que faa o teste passar (sem quebrar outros
testes) e tambm teremos segurana na Refatorao deste mesmo cdigo daqui a
alguns minutos.
Vale lembrar tambm daquela sequncia tima de desenvolvimento que devemos ter
na cabea: Cdigo que funciona -> Cdigo simples e limpo -> Cdigo rpido
Agora com a nova funcionalidade implementada e o teste passando, seguimos para a
prxima fase:

Refatorao
Agora sim! Voc purista da programao que condenou a minha gerao por eu ter
falado para abandonarmos as boas prticas de desenvolvimento, agora sim pode ficar
tranquilo!
Neste momento que vamos analisar melhor aquele cdigo que fizemos
simplesmente para o nosso Teste passar. neste momento que retiramos duplicidade,
renomeamos variveis, extramos mtodos, extramos Classes, extramos Interfaces,
usamos algum padro conhecido, etc. neste momento que podemos deixar o nosso
cdigo simples e claro e o melhor de tudo: Funcional!
Temos um teste agora que indicar qualquer passo errado que podemos dar ao
melhorar o nosso cdigo. E no somente este cdigo que acabamos de escrever. Aps
algum tempo com TDD, ser criada uma Suite de Testes, onde poderemos refatorar

sem a preocupao de atingir negativamente algum cdigo j existente, pois j


teremos Testes indicando qualquer falha.
J ouviu falar no SRP? "Single Responsibility Principle". Este princpio nos diz que
devemos ter somente um motivo para modificar uma classe. Ou seja, ele fala sobre
termos uma classe com somente uma responsabilidade.
Por que estou lembrando disso? Por que o TDD nos "fora" a termos classes seguindo
esta regra, pois facilita os Testes. No podemos refatorar um trecho de cdigo e
quebrar vrios Testes. Assim, este princpio acaba sendo utilizado, mesmo que voc
no perceba.
Pronto! Como acabamos a refatorao, o prximo passo o prximo Teste!

Sendo chato, parte II


No ltimo passo aplicamos a Refatorao, que a melhoria do cdigo. Isto se faz
necessrio(leia-se bom) sim em relao s boas prticas j conhecidas, porm com
TDD isso se torna obrigatrio!
Por que? Simples! Para escrevermos um Teste, escrevemos uma funcionalidade, mas
esta funcionalidade no pode quebrar outro Teste. Ao quebrar outro Teste, temos que
fazer um pequeno esforo para que o nosso novo cdigo esteja menos acoplado ao
cdigo restante.
Viu o que aconteceu? Trocamos a forma de desenvolvimento. Em vez de projetarmos
a nossa aplicao e tentarmos escrever um cdigo (levando horas) pensando nas
mudanas no futuro, j pensando nos Patterns, regras e etc, escrevemos um cdigo
de Teste que guiou a simplicidade do nosso cdigo, que em seguida refatoramos para
deix-lo menos acoplado e com uma maior coeso, fazendo com que este cdigo seja
facilmente modificado no futuro, seja para correo de problemas ou para novas
funcionalidades.

Mundanas seguras no cdigo


Muitos desenvolvedores ainda escrevem cdigo pensando nas modificaes no futuro
que este cdigo poderia ter e acaba escrevendo um cdigo com Factory, Singleton,

Template Method, Bridge, Strategy e todos os amigos do GoF. Este pensamento,


apesar de parecer seguro, contraria os princpios da Metodologia gil.
Claro que o software sempre sofrer mudanas e a nossa preocupao de hoje :
Prever/Escrever um cdigo/design para modificar no futuro quando precisarmos.
Mas deveria ser
Escrever um cdigo simples e claro, que seja fcil modificar e seguro.
Kent Beck, no seu livro tambm comenta sobre perdermos muito tempo imaginando
um Design que seja perfeito para aplicao e acabamos escrevendo um cdigo que na
maioria das vezes no era necessrio. Com TDD realmente abandonamos este
pensamento, justamente por queremos que o Teste passe logo, ou seja, escrevemos
o nosso cdigo da forma mais simples possvel.
Como conseguimos um cdigo simples? -> Fazendo um Teste passar
Como conseguimos um cdigo claro?

-> Refatorando o cdigo aps ele passar

Como conseguimos um cdigo seguro?

-> Com Testes

Documentao para o limbo?


No. Aquela documentao no papel, em uma wiki, em um doc, etc desatualizada
pois muito custoso atualiz-la a cada Refatorao/Mudana de cdigo. A melhor
documentao e mais atualizada possvel a Sute de Testes pois ela mostra de
forma simples como est funcionando o sistema naquele exato momento. Se voc
percorrer os testes voc entender o que o sistema realiza.

Hello World do TDD


Sim. Pra voc que est ansioso por algo um pouco mais complexo, realmente neste
momento no vamos ter. Assim podemos nivelar o conhecimento com um exemplo
trivial, afim de no deixar ningum perdido.
Vamos l!

Ambiente
Para o nosso "sistema" vou utilizar Java+Eclipse+JUnit. Imagino que voc j
conhea um pouco de Java e Eclipse, portanto no mostrarei a instalao deles pois
fugiremos do escopo do artigo. Caso tenham algum problema, s entrar em
contato.
Alexandre, posso fazer em C#? A vontade! Pode usar C#+NUnit. Posso usar Delphi? A
vontade! Pode usar Delphi+DUnit!
Primeiro exemplo
Este ser um exemplo bem trivial. No nosso "sistema" precisaremos criar uma
Calculadora que calcula as 4 operaes bsica: Adio, Subtrao, Multiplicao e
Diviso. Simples assim!
Passos para a criao do projeto:

Crie um novo projeto no Eclipse com o nome de "ArtigoTDD";

Crie um pacote com o nome "artigotdd.calculadora.teste".

Com a estrutura bsica criada, agora vamos criar a nossa primeira classe. A classe
Calculadora Alexandre? No! A classe CalculadoraTeste? Isso mesmo. Vamos fazer
um Teste em algo que ainda no temos implementado.
Assim, criamos a classe CalculadoraTeste no pacote criado:
package artigotdd.calculadora.teste;
public class CalculadoraTeste {
}

Tudo certinho por aqui. Agora devemos pensar sobre o nosso problema. Queremos
fazer algumas operaes nesta calculadora e para comearmos pensaremos em uma
soma. Como podemos testar uma soma?
Simples e trivial: Dados dois valores, o resultado deveria ser a soma deles.

Vamos ento escrever exatamente este Teste! Vamos criar um mtodo que indique
este teste. Para o JUnit entender que o mtodo "testvel", temos a anotao
"@Test" no mtodo (No esquea do import do JUnit).
Assim temos:
public class CalculadoraTeste {
public class CalculadoraTeste {
@Test
public void deveriaSomarDoisValoresPassados() throws Exception {
}
}

Isso mesmo. O nome do nosso mtodo deve mostrar exatamente o que ele est
querendo fazer. comum encontrar mtodos de teste comeando com "deveria" e
ingls voc tambm vai encontrar o "should".
Agora que temos o mtodo de teste, vamos indicar pra ele o que queremos. Vamos
agora inserir duas variveis e usar o mtodo "assertEquals" do prprio JUnit.
Como o prprio nome diz, o "assertEquals" indica que estamos querendo afirmar algo.
(No esquea do import: "import static org.junit.Assert.*").
Vamos ao cdigo:
public class CalculadoraTeste {
@Test
public void deveriaSomarDoisValoresPassados() throws Exception {
int valorA = 1;
int valorB = 2;
int soma = 0;
assertEquals(3, soma);
}
}

Pronto! Queremos o resultado 3 para a soma das variveis valorA e valorB. Acabamos
de escrever o Teste e bvio que ele no passa. Vamos rod-lo? Boto direito na classe
de teste -> Run As -> JUnit Test.
Barra vermelha, era o que temamos!

Figura 2: Teste falhou


Mas j espervamos pois este o ciclo: Test->Red->Green->Refactor.
E o que o Trace nos diz? Voc j ouviu essa frase do seu cliente? "Eu queria isso, mas
est fazendo aquilo". exatamente o que temos aqui:

Figura 3: Trace com motivo do teste ter falhado


No nosso Trace, o JUnit indica que esperava o valor 3 porm foi encontrado o valor 0.
E agora o nosso objetivo fazer o Teste passar! Colocamos agora a classe
responsvel pela implementao da funcionalidade:
public class CalculadoraTeste {
@Test
public void deveriaSomarDoisValoresPassados() throws Exception {
int valorA = 1;
int valorB = 2;
Calculadora calculadora = new Calculadora();
int soma = calculadora.soma(valorA, valorB);
assertEquals(3, soma);
}
}

Este cdigo nem mesmo compila! Sendo bem ortodoxo em relao ao TDD, realmente
este o nosso prximo passo. No criamos a classe pra depois us-la e sim usamos a
classe pra depois cri-la.
Agora que o compilador gentilmente com uma linha vermelha e um "x" vermelho nos
avisou do erro, basta criarmos a classe Calculadora. Vamos dar um passo um

pouquinho maior agora e tambm criar o mtodo "soma" na classe Calculadora,


recebendo dois inteiros:
public class Calculadora {
public int soma(int valorA, int valorB) {
return 0;
}
}

timo! Se rodarmos o nosso Teste, vamos ver a barra vermelha novamente. Isso por
que criamos o mtodo mas ele no implementa o que precisamos. Vamos agora
implementar:
public class Calculadora {
public int soma(int valorA, int valorB) {
return valorA + valorB;
}
}

Agora sim! Rodando o nosso Teste temos a sonhada barra verde:

Figura 3: Teste passou


Agora temos a ltima etapa do ciclo: Refatorao! Como um exemplo muito trivial
no temos aqui alguma refatorao interessante, vamos deixar a refatorao para
quando o nosso sistema crescer ou quando as funcionalidades forem modificadas e a
refatorao aparecer naturalmente.
Seguindo os mesmos passos anteriores, vamos criar agora o teste para a subtrao.
Desta vez no faremos passo a passo novamente, pois ser idntico ao mtodo
soma:
Teste da subtrao:

@Test
public void deveriaSubtrairDoisValoresPassados() throws Exception {
Calculadora calculadora = new Calculadora();
int valorA = 1;
int valorB = 2;
int soma = calculadora.subtrai(valorA, valorB);
assertEquals(3, soma);
}

Mtodo na classe Calculadora:


public int subtrai(int valorA, int valorB) {
return valorA - valorB;
}

A seguir falaremos sobre os Testes de Unidade em relao ao comportamento dos


nossos objetos. Tambm falaremos sobre o conceito de Mock de Objetos, que
extremamente importante no TDD.
J vou adiantar que voc ficar chateado com o que faremos para "Mockar" os nossos
objetos, mas prometo que em seguida ficaremos muito felizes com uma soluo bem
mais bacana para isso. Calma! o conceito de "Mockar" objetos aparecer logo logo!
Para iniciar este artigo, vamos fugir um pouco do conceito citado acima e fazer a
funcionalidade de diviso de dois nmeros para verificarmos a possibilidade de fazer
Testes esperando que alguma Exceo ocorra. Isso mesmo! Por algum motivo voc
deseja lanar uma Exceo caso algo de errado acontea e podemos fazer isso com o
JUnit, apresentado no artigo anterior.

Teste da diviso
A funcionalidade simples: Fazer a diviso de dois nmeros. Lembrando: um caso
simples e isolado onde a inteno voc imaginar um caso real da sua aplicao.
Ento vamos para o Teste!
Mas aqui comearamos com aquele conceito de BabySteps, onde faramos passos
curtos para chegarmos soluo, correto? Correto, porm os BabySteps no so uma
regra xiita que devemos seguir risca. Segundo o prprio Kent Beck em seu livro
sobre TDD, os BabySteps so para quando realmente no temos confiana suficiente
em escrever determinado cdigo. Como ele cita tambm, no devemos desenvolver

com BabySteps a todo momento e sim devemos ficar felizes por podermos faz-lo
quando desejarmos.
Agora que lembramos disso, vamos correr um pouquinho mais no cdigo e escrever
um pouco mais rpido que no artigo anterior, porm sinta-se vontade para colocar a
sua velocidade:
Adicionando o mtodo de Teste nossa classe de CalculadoraTeste:
public class CalculadoraTeste {
@Test
public void deveriaDividirDoisValoresPassados() throws Exception {
int valorA = 6;
int valorB = 2;
Calculadora calculadora = new Calculadora();
int divisao = calculadora.divide(valorA, valorB);
assertEquals(3, divisao);
}
}

E na nossa classe Calculadora, vamos escrever o nosso mtodo de produo:


public class

Calculadora {

public int divide(int valorA, int valorB) {


return valorA / valorB;
}
}

Legal! Agora podemos rodar o nosso Teste e v-lo passando. Uma observao
simples: No comentei nos artigos anteriores mas s para ter certeza que de
conhecimento de todos, vou comentar: Rodamos os nossos Testes clicando com o
boto direito na classe de Teste, selecionando Run As e em seguida selecionando
o JUnit Test.
Agora temos um Teste verde na nossa frente!

Figura 4: Teste passou aps refatorao


Maravilha! Finalizamos? No.
E quando esperamos por uma exceo?
Vamos atormentar os professores de matemtica e fazer a seguinte alterao na
nossa classe de Teste:
public class

CalculadoraTeste {

@Test
public void deveriaDividirDoisValoresPassados() throws Exception {
int valorA = 6;
int valorB = 0;

//diviso por zero!

Calculadora calculadora = new Calculadora();


int divisao = calculadora.divide(valorA, valorB);
assertEquals(?, divisao);
}
}

O que fizemos: atribumos o valor zero varivel valorB. E o que esperamos no nosso
assertEquals? No tenho noo! Podemos esperar tudo, menos um valor! Sendo
assim, na sua aplicao, voc poderia mostrar uma mensagem para o usurio
solicitando gentilmente que ele insira um valor coerente. E como podemos fazer um
Teste esperando uma exceo? Vamos l!

Teste esperando por uma exceo


Podemos usar um parmetro na prpria anotao do JUnit (@Test) para indicar qual a
exceo que estamos esperando receber. Assim teramos o seguinte cdigo para o
nosso Teste:

public class

CalculadoraTeste {

@Test
public void deveriaDividirDoisValoresPassados() throws Exception {
int valorA = 6;
int valorB = 3;
Calculadora calculadora = new Calculadora();
int divisao = calculadora.divide(valorA, valorB);
assertEquals(2, divisao);
}
@Test
public void deveriaLancarUmaExcecaoIndicandoFalhaAoDividirUmNumeroPorZero()
throws Exception {
int valorA = 6;
int valorB = 0;
Calculadora calculadora = new Calculadora();
int divisao = calculadora.divide(valorA, valorB);
assertEquals(0, divisao);
}
}

Mas infelizmente ao rodar, temos uma barra vermelha:

Figura 5: Teste falhou aps modificaes


Agora sim podemos fazer o nosso Teste passar adicionando o parmetro anotao
do JUnit (@Test):
public class CalculadoraTeste {

@Test(expected = ArithmeticException.class)
public void deveriaLancarUmaExcecaoIndicandoFalhaAoDividirUmNumeroPorZero()
throws Exception {
int valorA = 6;
int valorB = 0;
Calculadora calculadora = new Calculadora();
calculadora.divide(valorA, valorB);
}
}

E agora sim temos a barra verde para o nosso Teste:

Figura 6: Teste aprovado aps ajustes


Este foi um caso simples para mostrar como possvel trabalhar com Testes que
devem verificar excees. Podemos estender este conceito para outros momentos,
como fazer um Teste que no deveria esperar uma exceo que j esta poderia ser
tratada pela nossa classe Calculadora, pois fazer da forma acima no to
interessante. Agora vamos melhorar os nossos testes!

Indo mais alm


At agora fizemos testes bem simples e a idia imaginarmos outras funcionalidades
em nossas aplicaes reais de forma a desenvolv-las desta forma.Claro que uma
classe do tipo Calculadora no o melhor dos exemplos, mas optei pela simplicidade
at agora.
De qualquer forma, tenho certeza que sua aplicao poder ter muitas
funcionalidades que, se bem isoladas, sero tambm passveis de testes semelhantes.

Agora podemos avanar um pouco mais! Comeamos o artigo com uma nova palavra:
"Mock".
Definio simples: Um Mock basicamente um objeto falso, que capaz de simular
as dependncias de um objeto e capaz de simular determinadas aes desse objeto.
Por que usado? Para testar o comportamento de outros objetos desejados.
Por que gostaramos de testar o comportamento de outros objetos? Justamente para
termos certeza de que tudo ocorreu conforme pensamos. Vamos imaginar a seguinte
situao: Temos uma aplicao onde cada vez que exclumos uma pessoa, um log
gerado no banco no banco de dados com o nome da pessoa que foi excluda.
Como poderamos ter certeza que a gerao do log realmente vai ser
chamada e que nada de ruim acontecer no caminho?
Podemos fazer este teste usando exatamente um Mock da classe de Log. Vamos fazer
o cdigo mais simples que vier na nossa cabea:
//Classe do nosso Teste
public class PessoaTeste {
@Test
public void deveriaCriarUmLogQuandoUmaPessoaForExcluida()
throws Exception {
Pessoa pessoa = new Pessoa();
pessoa.setNome("Alexandre");
PessoaController pessoaController = new PessoaController();
pessoaController.exclui(pessoa);
// Como saberemos se realmente o "criaLog" ser chamado?
}
}
//Nosso Controller
public class PessoaController {
private PessoaDAO pessoaDAO;
private Log log;
public PessoaController() {
pessoaDAO = new PessoaDAO();
log = new Log();
}
public void exclui(Pessoa pessoa) {
PessoaDAO.exclui(pessoa);
log.criaLog(pessoa.getNome());

}
}
//Nossa classe de criao de Logs
public class Log {
public void criaLog(String nomeDaPessoa) {
// Cdigo para criar um Log no banco, em um txt, etc...
}
}

Eu sei, eu sei. Abandonamos aqui algumas boas prticas mas em prol de um


entendimento melhor da situao. Logo logo vamos melhorar um pouco mais este
cdigo.
Voltamos mesma pergunta: Como vamos saber se a criao do Log foi chamada?
Vamos criar um Mock da nossa classe de criao de Logs!
O nosso Mock deve simular o funcionamento da funcionalidade, ou seja, ele no
conter cdigo algum, ser apenas para verificarmos se ele foi chamado.
Uma idia seria criarmos uma classe que estende da nossa classe de Log, mas
seremos um pouquinho melhores que isso e vamos criar uma Interface para
implementarmos.
Assim poderamos ter:
//Nossa Interface de criao de Logs
public interface GeradorDeLog {
public void criaLog(String nomeDaPessoa);
}

Podemos ento ter a seguinte classe horrorosa:


//Mock da nossa classe de Log
public class LogMock implements GeradorDeLog {
private String nome;
@Override
public void criaLog(String nomeDaPessoa) {
this.nome = nomeDaPessoa;
}
public String getNome() {
return nome;

}
}

Mas temos um detalhe: O nosso Controller est com uma dependncia forte que a
classe Log sendo instanciada diretamente pelo Controller. Isso impossibilita o uso do
nosso Mock. Ento vamos melhorar um pouquinho o Design da nossa aplicao. Opa!
Olha o TDD nos "obrigando" a melhorar o Design da nossa aplicao!
Vamos ento aplicar um princpio bem importante que a Inverso de
Controle atravs da Injeo de nossas Dependncias. Vamos enviar ento o
nosso GeradorDeLog para o Controller atravs do construtor.
Assim teremos:
//Nossa classe de Teste
public class PessoaTeste {
@Test
public void deveriaCriarUmLogQuandoUmaPessoaForExcluida()
throws Exception {
Pessoa pessoa = new Pessoa();
pessoa.setNome("Alexandre");
LogMock nossoLogMock = new LogMock();
PessoaController pessoaController = new PessoaController(nossoLogMock);
pessoaController.exclui(pessoa);
assertEquals(pessoa.getNome(), nossoLogMock.getNome());
}
}
//Nosso Controller
public class PessoaController {
private PessoaDAO pessoaDAO;
private GeradorDeLog log;
public PessoaController(GeradorDeLog log) {
this.pessoaDAO = new PessoaDAO();
this.log = log;
}
public void exclui(Pessoa pessoa) {
PessoaDAO.exclui(pessoa);
log.criaLog(pessoa.getNome());
}
}

E olha que temos aqui, um teste passando!

Figura 7: Novo teste aprovado

Recapitulando
Muitas vezes precisamos testar o comportamento dos nossos objetos. No nosso caso
qual o comportamento? A criao de um Log quando uma pessoa excluda. Mas
no queremos criar um Log de verdade quando fizermos o teste de excluso e sim
queremos verificar se o mtodo da criao do Log foi chamado.
Para fazermos isso, usamos um objeto "Mockado" que um objeto que simula o
comportamento do nosso objeto. , a grosso modo, um objeto falso que no tem
inteligncia.
Assim, pelo Teste, na excluso de uma pessoa um Log gerado pois ao chamar o
mtodo de excluso, o mtodo de criao do Log tambm chamado, ou seja, nada
de errado acontece pelo caminho.
Para uma viso geral do nosso Teste, vamos listar os nossos passos:

Criamos a nossa classe de Teste PessoaTeste;

Criamos as classes: PessoaController, Log, Pessoa;

Sentimos dificuldade para fazer o teste no Controller pois ele estava muito
acoplado com a classe de Log;

Criamos a Interface GeradorDeLog para a nossa classe de Log implement-la;

Fizemos a nossa classe LogMock tambm implementar a Interface


GeradorDeLog;

Passamos para o nosso Controller a nossa classe de Log "Mockada", por Injeo
de Dependncia pelo construtor;

Identificamos pelo assertEquals se o mtodo de gerao de Log foi realmente


invocado, verificando se o nome no Log era o mesmo nome da Pessoa.

Finalizando
Legal! Conseguimos fazer o nosso Teste rodar, melhoramos um pouco o Design da
nossa Aplicao aplicando a Inverso de Controle (mas podemos refatorar para algo
bem melhor, claro!) mas acabamos ficando com essa classe horrorosa que
a LogMock.
Mas esta idia no s feia! Essa idia s poder ser usada caso tenhamos objetos
simples. Imagine que temos um objeto que instancia outro objeto e este tambm
instancia outro objeto e cada um tem diversos mtodos. A nossa vida se resumiria a
criar classes de Mock e isso no legal.
neste ponto que podemos usar frameworks para isso. Podemos "Mockar" as nossas
dependncias atravs destes Frameworks sem precisar criar outras classes para isso!
O desenvolvedor de hoje realmente tem que dominar a tcnica que, apesar de
parecer nova, desde os primrdios da civilizao Inca! O seu software funciona?
Sim? Mas no tem testes? Ento voc no tem garantia alguma que ele funciona!

Alexandre Gama
Computao e Matemtica na USP, com breve passagem pela Poli(USP) e pelo BCC(USP). Empreendedor e lder tcnico
na AGR Comunicao Digital e scio e lder tcnico na Digiminds Group. Desenvolveu diversos projetos em Java, Delphi
[...]

Leia mais em: Test Driven Development: TDD simples e


prtico http://www.devmedia.com.br/test-driven-development-tdd-simples-epratico/18533#ixzz3am0BxyOm

You might also like