TDD – Test-Driven Development + C# + Mocks – Parte IV

AGRADEÇA AO AUTOR COMPARTILHE!

Fala galera, beleza?

Dando continuidade a nossa série de artigos sobre TDD, vamos hoje falar de um ponto muito interessante: Mocks.

Artigos anteriores:

Parte I – http://viniciusquaiato.com/blog/index.php/tdd-test-driven-development-c/
Parte II – http://viniciusquaiato.com/blog/index.php/tdd-test-driven-development-c-parte-ii/
Parte III – http://viniciusquaiato.com/blog/index.php/tdd-test-driven-development-c-parte-iii/

De uma maneira resumida mocks são objetos que simulam o comportamento de outros objetos. Desta forma quando testamos com mocks geralmente testamos o comportamento entre os objetos e não os seus estados.

Utilizar mocks é algo muito interessante, pois eles nos permitem realizar testes com objetos “de mentira” ao invés de objetos que temos implementados como bancos de dados, serviços, web services, por exemplo. Ou seja, conseguimos verificar o comportamento de nossos objetos de negócios quando relacionados com objetos/ações que não temos o controle para testar (por exemplo uma ida ao web service), ou que são muito pesadas para colocar em unit testes automatizados (idas ao banco, chamadas a um webservice, envios de e-mail, etc).

Vamos continuar nosso exemplo das contas bancárias, e imaginar que a cada operação se depósito realizada é necessário utilizar um webservice do banco para registrar um log da ação.

Não vou abordar a criação do Web Service, e de fato nem vou utilizar um, vamos apenas definir que ele implementa a seguinte interface que recebe uma string com o nome da operação e o valor:

Para trabalhar com mocks neste post utilizarei o framework Moq(lê-se Mock you), que pode ser baixado aqui: http://code.google.com/p/moq/

Para que possamos adicionar este comportamento às nossas contas(fazer chamada ao webservice), precisaremos alterar o código que já funciona, desta forma os testes que já existem nos ajudarão a saber se algo novo quebrou algo que já estava funcionando.
Vamos começar criando um teste de depósito que deverá fazer uma chamada para o webservice:

Este teste por enquanto só está realizando o depósito, precisamos então garantir que o webservice seja chamado, e é utilizando um mock que faremos isso.

Adicione a dll do Moq ao seu projeto:

Adicionar Dll Moq

Adicionar Dll Moq

Agora vamos criar nosso primeiro mock e também incluir a referência na classe de testes:

e

Podemos perceber que, agora, estamos com um problema. Nossa classe de contas não possui uma instância de IWebServiceContas, e não me parece uma boa solução passar esta instância para o método Depositar. Sendo assim, vamos deixar este teste “pausado” por enquanto.
Para fazermos isso basta colocarmos o Attribute [Ignore] antes do teste e ele não será executado:

Vamos alterar nossos testes para que garantam que a nossa conta receba uma instância de IWebServiceContas(injeção de dependância) no seu construtor, para isso vamos alterar nosso primeiro teste bem como o seu nome (lembrem, os testes devem evoluir junto com o código e com o modelo do domínio):

Ok, agora nada compila. Perfeito! Vamos alterar nossa classe para que ela receba um objeto que implemente IWebServiceContas:

Como todos os nossos testes criam instâncias de contas, vamos atualizá-los para que compilem e então vamos executar todos os testes:

Executando todos os testes

Executando todos os testes

Agora podemos voltar ao teste que estávamos escrevendo, afinal nossa alteração não quebrou nada.
Para que configuremos nosso mock para atuar como o webservice precisamos fazer seu setup (linhas 5 e 6), e depois verificamos se tudo ocorreu como esperado (linha 11):

O que fizemos nas linhas 5 e 6 foi dizer ao mock “hey mock! Espero que o método RegistrarOperacaoEmConta seja chamado com os argumentos “Depósito” e 10″.
Depois na linha 11 dizemos para ele “hey! Verifique se tudo o que eu configurei aconteceu aê!”.

Desta forma nosso mock se comporta como se fosse o webservice, ele está imitando o comportamento do webservice através de sua interface IWebServiceContas, com a enorme vantagem de que temos total controle sobre o mock e não estamos dependentes do webservice e nem de uma implementação concreta para testar.

Se rodarmos nossos testes teremos:

Executand teste com Mock do Moq

Executand teste com Mock do Moq

É bem simples o que aconteceu, configuramos nosso mock dizendo que o método RegistrarOperacaoEmConta seria chamado, no entanto esse método não foi chamado. Para isso precisamos alterar nossa classe conta, para que de fato ela chame o método do webservice (ou melhor, do objeto que implementa a interface do webservice).
Porém quando passamos o parâmetro no construtor da nossa classe de ContaBancaria não armazenamos este objeto em um campo da classe. Vamos então criar uma propriedade privada deste tipo na classe conta bancária(linhas 5 e 9):

Agora que fizemos uma alteração, vamos rodar todos os testes e ver se tudo está ok. O resultado deve ser de que tudo está funcionando perfeitamente.

Agora vamos retomar a alteração do método Depositar para que ele passe no teste do mock:

E quando rodamos os testes obtivemos vários erros:

Rodando Teste Mock

Rodando Teste Mock

Isso ocorreu pois eu alterei meus testes passando null para o parâmetro do IWebServiceContas, desta forma vou acrescentar uma validação extra ao meu construtor, garantindo que o serviço esteja presente na construção da conta (vamos imaginar que é uma regra):

E agora altero o construtor para respeitar a regra:

E rodando os testes vamos ver que só falharam os lugares onde eu mantive o construtor passando null, ou seja, sem respeitar a regra, desta forma vou atualizar os testes para respeitar.

Agora vou executar novamente todos os testes e ver o que aconteceu:

Rodando Todos Testes e Teste Mock

Rodando Todos Testes e Teste Mock

Pronto agora podemos ver que até mesmo nosso teste com o mock está funcionando!

O que isso quer dizer? Quer dizer que a nossa classe ContaBancaria está se comportando como esperado. Está enviando uma chamada para o objeto webservice, passando os parâmetros corretamente no momento em que é feito um depósito.
Para garantir isso, vamos escrever mais um teste, passando outros valores:

[TestMethod] public void Deve_Realizar_Deposito_15_E_Chamar_WebService_Passando_Deposito_E_Valor_15() { var mockWebService = new Mock(); mockWebService .Setup(ws => ws.RegistrarOperacaoEmConta(“Depósito”, 15));

Este teste irá falhar, pois eu deixei hardcoded a chamada para o webservice dentro do método, para isso vamos alterar a chamada do método:

E finalmente, rodando nossos testes:

Rodando Todos Testes e Testes com Mocks passando

Rodando Todos Testes e Testes com Mocks passando

Bom galera é isso.
Sei que existe muito mais complexidade em realizar testes, usar mocks, etc. E sei também que TDD não é algo que podemos sair aplicando da noite para o dia. É preciso maturidade. No entanto a maturidade só virá com a prática e a utilização.
Corroborando o Giovanni Bassi: “Testar é caro, não testar é mais caro ainda!”.

Neste post vimos como usar mocks, e também vimos como os testes nos ajudaram a encontrar erros, afinal, fizemos algumas alterações no código e alguns testes pararam de rodar. É disso que eu estou falando! É para isso que os testes servem!

Original em Vinicius Quaiato: http://www.viniciusquaiato.com

Qualquer dúvida podem entrar em contato comigo.

Abraços,
Vinicius Quaiato.

AGRADEÇA AO AUTOR COMPARTILHE!

4 Comentários

Vinicius Quaiato
2

#

Vinicius Quaiato disse:

11/11/2009 às 10:30

Poxa, o código saiu errado. onde é new Mock() na verdade deveria ser:
new Mock<IWebServiceContas>();

Acho que ele interpretou o generics como tag HTML…

No meu blog ficou certo: http://www.viniciusquaiato.com

Att,
Vinicius Quaiato.

Deixe seu comentário

Seu endereço de e-mail não será publicado. Campos com * são obrigatórios!

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">