Test Driven Development: TDD simples e prticoVeja 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 sobreTDD, 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
simplesTDD 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 mestreKent
Beck(Autor tambm do famoso livro sobre TDD, que recomendo) e um dos
pilares (l-se prticas) doExtreme 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
desenvolvimentoRed,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 TDDNs 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 usoTemos 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.MitoMuitos 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 motivosA 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 ISei 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 naFigura
1:Vamos entender um pouco sobre cada etapa:Novo TesteEste 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 noCodificamos 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 daremospassos 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
quevoc encontrar.Teste FalhandoNeste 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
funcionalidadeJ ouviu falar noKISS?"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
rpidoAgora com a nova funcionalidade implementada e o teste
passando, seguimos para a prxima fase:RefatoraoAgora 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 tempocom 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 noSRP?"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 IINo 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, escrevemosum 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
cdigoMuitos 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 doGoF. 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 serEscrever um cdigo simples e claro, que seja fcil
modificar e seguro.Kent Beck,no seu livrotambm 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 passarComo conseguimos um
cdigo claro? -> Refatorando o cdigo aps ele passarComo
conseguimos um cdigo seguro? -> Com TestesDocumentao 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 TDDSim. 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!AmbientePara o nosso "sistema" vou utilizarJava+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 exemploEste 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
umTesteem 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 {@Testpublic 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 {@Testpublic 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 variveisvalorAevalorB. Acabamos de escrever o Teste e bvio
que ele no passa. Vamos rod-lo? Boto direito na classe de teste
-> Run As -> JUnit Test.Barravermelha, era o que temamos!
Figura 2:Teste falhouMas 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 falhadoNo nosso Trace, o
JUnit indica que esperava o valor3porm foi encontrado o valor0.E
agora o nosso objetivo fazer o Teste passar! Colocamos agora a
classe responsvel pela implementao da funcionalidade:public class
CalculadoraTeste {@Testpublic 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
barraverde:
Figura 3:Teste passouAgora 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:@Testpublic 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 divisoA
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 prprioKent 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
{@Testpublic 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,
selecionandoRunAse em seguida selecionando oJUnit Test.Agora temos
um Teste verde na nossa frente!
Figura 4:Teste passou aps refatoraoMaravilha! 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 {@Testpublic 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 valorzero
varivelvalorB. E o que esperamos no nosso assertEquals? No tenho
noo! Podemos esperar tudo, menosum 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
exceoPodemos 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 {
@Testpublic void deveriaDividirDoisValoresPassados() throws
Exception { int valorA = 6;int valorB = 3;Calculadora calculadora =
new Calculadora();int divisao = calculadora.divide(valorA,
valorB);
assertEquals(2, divisao);}
@Testpublic 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 modificaesAgora sim podemos fazer o
nosso Teste passar adicionando o parmetro anotao doJUnit
(@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 ajustesEste 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 classeCalculadora,pois fazer da forma acima
no to interessante. Agora vamos melhorar os nossos testes!Indo mais
almAt 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 Testepublic class PessoaTeste {@Testpublic
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
Controllerpublic 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 Logspublic 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 deLog, mas seremos um pouquinho melhores que isso e vamos
criar uma Interface para implementarmos.Assim poderamos ter://Nossa
Interface de criao de Logspublic interface GeradorDeLog {public
void criaLog(String nomeDaPessoa);}Podemos ento ter a seguinte
classe horrorosa://Mock da nossa classe de Logpublic class LogMock
implements GeradorDeLog {
private String nome;
@Overridepublic 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 classeLogsendo
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
aInverso de Controleatravs da Injeo de nossas Dependncias. Vamos
enviar ento o nossoGeradorDeLogpara o Controller atravs do
construtor.Assim teremos://Nossa classe de Testepublic class
PessoaTeste {@Testpublic 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
Controllerpublic 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 aprovadoRecapitulandoMuitas 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.FinalizandoLegal! 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 aLogMock.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
prticohttp://www.devmedia.com.br/test-driven-development-tdd-simples-e-pratico/18533#ixzz3am0BxyOm