Ayende costuma dizer que sempre que “sente dor” ao utilizar uma ferramenta ele parte para implementar sua própria. Uma estratégia um pouco radical, mas com o Rhino Mocks ele acertou na mosca. A ferramenta acabou tornando-se a queridinha da comunidade .Net agile. Apesar de ter um funcionamento semelhante ao de suas predecessoras, esta ferramenta trouxe muitas melhorias na usabilidade.
Veja como fica o teste do método Transferir utilizando o Rhino para criar os Stubs
[Test]
public void Transferir_ComSaldo()
{
decimal montante = 500;
int idContaDebito = 1;
Conta contaDebito = new Conta( idContaDebito, 1000 );
decimal saldoEsperadoContaDebito = contaDebito.Saldo - montante;
int idContaCredito = 2;
Conta contaCredito = new Conta( idContaCredito, 500 );
decimal saldoEsperadoContaCredito = contaCredito.Saldo + montante;
MockRepository mockery = new MockRepository();
IRepositorio<Conta> repositorioMocked = mockery.Stub<IRepositorio<Conta>>();
SetupResult.For( repositorioMocked.Buscar( idContaDebito ) )
.Return( contaDebito );
SetupResult.For( repositorioMocked.Buscar( idContaCredito ) )
.Return( contaCredito );
mockery.ReplayAll();
ServicoConta servico = new ServicoConta();
servico.Repositorio = repositorioMocked;
servico.Transferir( idContaDebito, idContaCredito, montante );
Assert.AreEqual( saldoEsperadoContaDebito, contaDebito.Saldo );
Assert.AreEqual( saldoEsperadoContaCredito, contaCredito.Saldo );
}
Relembrando, este é teste de estado, portanto, não preciso controlar nem validar as chamadas realizadas no stub. Preciso apenas garantir que ele retorne as contas correntes que o teste precisa.
A principal diferença entre o teste feito utilizando o NMock, da parte 3, e este, utilizando o Rhino, é a forma de definir o retorno do stub. Com o NMock devemos criar um mock e definir o retorno da seguinte forma:
Stub.On(repositorioMocked)
.Method("Buscar")
.With(idContaDebito)
.Will(Return.Value(contaDebito));
Já com o Rhino, o código fica assim:
SetupResult.For(repositorioMocked.Buscar(idContaDebito))
.Return(contaDebito);
Além de mais simples, a chamada do Rhino não utiliza strings, você define a chamada realizando a própria chamada! Isto facilita o uso de ferramentas de rafactoring, do intellisense e ainda valida o código em tempo de compilação.
A outra diferença é que o Rhino exige que você mude explicitamente do “modo” de definição para o modo de execução. Isto é feito pela seguinte chamada:
O outro teste que fiz foi o do método CadastrarCliente. Utilizando o Rhino, o teste fica assim:
[Test]
public void CadastrarCliente_CpfOk()
{
string cpf = "00000000000";
string sender = "email@domainname.com";
string body = "texto";
string subject = "titulo";
string emailCliente = "emailCliente@domainname.com";
Cliente cliente = new Cliente(cpf);
cliente.Email = emailCliente;
MockRepository mockery = new MockRepository();
IMailer mailerMocked = mockery.CreateMock<IMailer>();
IRepositorio<Cliente> repositorioMocked = mockery.CreateMock<IRepositorio<Cliente>>();
IValidacao validadorMocked = mockery.CreateMock<IValidacao>();
using ( mockery.Ordered() )
{
Expect.Call( delegate { validadorMocked.ValidarCpf( cliente.Cpf ); } );
Expect.Call( delegate { repositorioMocked.Salvar( cliente ); } );
Expect.Call( delegate { mailerMocked.EnviarEmail( cliente.Email, subject, body, sender ); } );
}
mockery.ReplayAll();
ServicoCliente servico = new ServicoCliente();
servico.Mailer = mailerMocked;
servico.Repositorio = repositorioMocked;
servico.Validador = validadorMocked;
servico.CadastrarCliente(cliente);
mockery.VerifyAll();
}
Neste teste verifico as chamadas realizadas e a ordem delas. A parte que muda entre os dois é novamente a definição das expectativas. Com o NMock o código ficava assim:
using(mockery.Ordered)
{
Expect.Once
.On(validadorMocked)
.Method("ValidarCpf")
.With(cliente.Cpf);
Expect.Once
.On(repositorioMocked)
.Method("Salvar")
.With(cliente);
Expect.Once
.On(mailerMocked)
.Method("EnviarEmail")
.With(cliente.Email, subject, body, sender);
}
//Chamada do método testado
mockery.VerifyAllExpectationsHaveBeenMet();
Já com o Rhino o código fica
using ( mockery.Ordered() )
{
Expect.Call( delegate { validadorMocked.ValidarCpf( cliente.Cpf ); } );
Expect.Call( delegate { repositorioMocked.Salvar( cliente ); } );
Expect.Call( delegate { mailerMocked.EnviarEmail( cliente.Email, subject, body, sender ); } );
}
mockery.ReplayAll();
//Chamada do método testado
mockery.VerifyAll();
Novamente o código com Rhino fica mais enxuto, legível e com todas as vantagens já citadas acima.
Em breve veremos outras alternativas interessantes de Mocking. Até lá.