23
Test Driven Development: TDD simples e prático Veja nesse artigo uma forma simples e prática o Desenvolvimento Orientado a Testes (TDD) e suas vantagens em relação aos métodos 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 idéia. Este é o primeiro de uma série de artigos que terão uma análise mais profunda onde resolveremos alguns problemas com TDD. Definição simples TDD Alexandre? O que é? TDD é o Desenvolvimento Orientado por Testes (Test Driven Development). Isso mesmo! Desenvolvemos o nosso software baseado em testes que são escritos antes do nosso código de produção! 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 idéia do TDD já é antiga e foi firmada com o mestre Kent Beck (Autor também do famoso livro sobre TDD , que recomendo) e é um dos pilares (lê- se práticas) do Extreme Programming !

Test Driven Development.docx

Embed Size (px)

Citation preview

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