Você está efetuando os testes sobre sua camada de negócios, e alguns deles farão alterações no banco de dados, como a inclusão de um novo cliente, como mostrado no primeiro exemplo desta série, embora ele tenha sido muito tosco, conforme já admiti no post anterior. O que te incomoda são todos os vermelhos que irão surgir quando os testes tentarem fazer a inclusão de um cliente que já existe, e aí você vai lembrar que tem que limpar a base de testes sempre antes de executá-los.
E quando executar todos os testes, precisará verificar se os arquivos de Log foram devidamente criados no disco. Se os e-mails que o sistema deveria enviar foram devidamente enviados. E tantas outras evidências a colher em tantos lugares que não fazem parte do escopo de seus testes.
No fim das contas, tudo o que você precisa saber é se o seu software está se comportando como deveria. E você pode fazer isso executando um select na tabela de customers pra saber se o teste realmente inseriu alguém lá, ou pode simplesmente procurar algum meio de garantir que seu código faz isso sem que isso realmente ocorra.
Conheça o uso de Mocks.
Mocks são objetos criados para fingir que são objetos que serão usados pelo seu teste, afim de fazer com que o objeto que você está testando funcione de forma transparente usando os Mocks no lugar dos objetos que esperaria usar. Ou seja, invés da minha classe de negócio criar uma instancia de uma classe de repositório, ela cria uma instancia de um Mock que se finge de repositório.
Mais do que isso, o Mock não somente se finge de repositório, como ainda monitora todas as interações com ele. Por exemplo, dizemos ao Mock que ele precisa ficar atento para receber o chamado ao método Add, e ele observa se este método é de fato chamado. Se não for chamado, ele lança uma exceção e o teste dá vermelho.
Entenderam?
Não?
Vamos supor que você esteja fazendo um teste no seu controller. Ele tem um método InsertCustomer que recebe um objeto Customer. Você sabe que ele tem que criar uma instancia do objeto de negócio, executar o método Insert do objeto e fazer um log em um arquivo texto.
Ora, acabamos de falar exatamente o que seu controller tem que fazer quando o método InsertCustomer for invocado, não? Então, ao término do teste, precisamos ter um registro inserido no banco de dados e um arquivo de texto com as informações de Log, certo?
Não! Não necessariamente!
Tudo o que você precisa é se preocupar se o controller se comportou como deveria, e para ter certeza de que o comportamento do controller está sendo o esperado, você não é forçado a realmente inserir uma linha no banco de dados e criar um arquivo texto. Você só precisa abstrair este comportamento em objetos separados, criar um mock que simulará estes objetos e monitorar o mock para certificar-se de que o seu controller está se comportando como deve.
No exemplo a seguir usarei o Rhino Mocks, mas, assim como no caso do MsTests, existem diversas ferramentas Mock para serem usadas. Provavelmente escreverei um Post mais tarde para comparar os principais frameworks disponíveis.
É assim que queremos testar nosso controller:
Mas para este teste dar certo, nós teremos que implementar a camada de Negócio, o que significaria iniciar um novo ciclo antes de testar o controller. Se seguirmos este percurso, veremos que teríamos que implementar também a camada de acesso a dados para testar a camada de Negócio para, só então, finalmente, testar o controller. Não é bem isto que queremos com o TDD. Com o TDD queremos começar de cima para baixo. Queremos começar testando o controller para programá-lo, quando ele estiver pronto, testaremos a camada de negócio e a programamos, quando ela estiver pronta, testaremos o controller de novo pra saber se nada afetou seu funcionamento… e assim por diante. De cima pra baixo.
Como testar o controller sem precisar ter a camada de negócio pronta? E como ter certeza de que o controller também está criando as entradas no Log?
É aí que entram os Mocks. Para garantir que o controller possa ser testado sem ter que implementar a camada de negócios e o Logger, mockaremos os objetos que farão o papel dos dois.
Mas o mock precisa conhecer algo a respeito do que estará simulando, para isso construiremos interfaces que representarão os nossos objetos de negócio e de Logger. Este é um aspecto interessante do uso de Mocks, ele nos induz a abstrair alguns objetos que causam dependência em outros. Como o bom funcionamento do controller depende do bom funcionamento do objeto de negócio e do logger, temos uma dependência. E o uso do Mock para suprir esta dependência exige uma interface, e esta interface abstrai a dependência em um tipo não concreto, ou seja, usando interface, temos a idéia de uma classe sem ter nenhuma noção de como ela será realmente implementada.
Pronto, temos as duas interfaces prontas. Não precisamos implementar nenhuma delas no momento, já que nosso foco está no controller. Então, vamos criar os mocks.
Observe que para criarmos os Mocks usamos o método GenerateStrictMock. No próximo Post explicarei os tipos de Mocks que podem ser criados com o Rhino.
Agora, precisamos ensinar aos Mocks o que eles devem monitorar.
O método Record() no MockRepository faz com que ele comece a gravar tudo o que você faz nos Mocks criados pelo Repositório em questão. O método ReplayAll() faz com que o repositório espere que tudo o que ele gravou seja devidamente reproduzido. O método VerifyAll() (no fim do código) faz o repositório verificar se tudo o que devia ser reproduzido aconteceu como esperado.
No caso, se rodarmos o teste agora temos um vermelho. Por que? Por que nada do que esperamos que seja reproduzido está sendo. Se copiarmos o código que é executado logo após o Record() após o ReplayAll(), o método VerifyAll() irá dizer que tudo aconteceu como esperado. Experimente.
Mas não é isso que queremos, é? Não. Queremos testar o comportamento do controller. Então agora é hora de implementarmos o código no controller. Para isto, precisaremos que o controller acesse os mocks, e aqui entra um ponto importante. Nós criamos os mocks que simulam objetos dos quais o controller tem dependência mas não estão implementados ainda (Logger e CustomerBusiness), para isso criamos duas interfaces (ILogger e ICustomerBusiness). Até aqui tudo bem.
O problema é que precisamos que o controller tenha a referencia aos mocks que criamos. Para isto existe uma técnica chamada Injeção de Dependência (Dependency Injection) mas esta técnica está fora do escopo desta série de TDD e falaremos disso em outra ocasião. No momento faremos a injeção da dependência no código mesmo através do construtor.
Remova o código que você duplicou no ReplayAll() e rode os testes novamente.
Deu verde? Não? Isso é por que a evidência que colhíamos era baseada no Id do Customer ser diferente de zero. Não precisamos desta evidência, remova a linha que faz o Assert. O Mock garante que o Controller invoca os métodos esperados e esta é a melhor evidência que temos, a evidência de que, independente do resultado, seu Controller funciona como você espera. Deu verde agora?
No próximo Post exploraremos mais o uso de Mocks, este foi só pra dar uma introdução. Até lá.
Stay sharp.






0 Respostas para “TDD – Parte 03: Saiba como seu código se comporta”