Wednesday, January 30, 2008 2:55 AM

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:

mockery.ReplayAll();

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á.

Comments

No comments posted yet.
Post Comment
Title *
Name *
Email (never displayed)
Website
Comment * (Allowed tags: blockquote, a, strong, em, p, u, strike, super, sub, code)  
Please add 8 and 7 and type the answer here: