Tuesday, January 22, 2008 8:25 PM

Construído a cara e semelhança do jMock, o NMock foi uma das primeiras ferramentas de dynamic mock object. Se tornou o mais utilizado pela comunidade .Net e, ainda hoje, mesmo com novos concorrentes na área, é bastante utilizado.

Com uma ferramenta de mock é possível:

  • Criar dinamicamente objetos, que implementam interfaces, que substituem objetos reais
  • Especificar retornos para métodos de objetos “mockados”
  • Definir expectativas dos métodos a serem chamados e sua ordem.

Em resumo, estas ferramentas substituem todo o código que fiz no post anterior, quando tentei criar meus próprios Mocks e Stubs. A vantagem é que utilizando uma ferramenta não tenho código-fonte para manter e ganho mais funcionalidades.

OBS: Não vou tratar da instalação das ferramentas, cada um se vire por ai para instalar e configurá-las :)

Agora vou refazer o teste do método Transferir, que fiz no 1º capítulo, utilizando o NMock. Sempre que precisar simular um objeto, tenho que criar um mock, utilizando o Mockery.

Mockery mockery = new Mockery();
IRepositorio<Conta> repositorioMocked = mockery.NewMock<IRepositorio<Conta>>();

Como este é um teste de estado não preciso controlar a ordem de execução dos métodos, apenas estabelecer o comportamento. Para isto, basta utilizar o Stub e definir o retorno de um método ou propriedade, dado os parâmetros de entrada.

Stub.On(repositorioMocked)
    .Method("Buscar")
    .With(idContaDebito)
    .Will(Return.Value(contaDebito));

Com o código acima, estou definindo que toda vez que o método Buscar for chamado com o parâmetro idContaDebito retornará o objeto contaDebito. O teste completo fica da seguinte maneira.

[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;

  Mockery mockery = new Mockery();
  IRepositorio<Conta> repositorioMocked = mockery.NewMock<IRepositorio<Conta>>();

  Stub.On(repositorioMocked)
      .Method("Buscar")
      .With(idContaDebito)
      .Will(Return.Value(contaDebito));

  Stub.On(repositorioMocked)
      .Method("Buscar")
      .With(idContaCredito)
      .Will(Return.Value(contaCredito));

  ServicoConta servico = new ServicoConta();
  servico.Repositorio = repositorioMocked;

  servico.Transferir(idContaDebito, idContaCredito, montante);

  Assert.AreEqual(saldoEsperadoContaDebito, contaDebito.Saldo);
  Assert.AreEqual(saldoEsperadoContaCredito, contaCredito.Saldo);
}

Com isto, “jogo fora” todo o código de objetos fakes que tinha e ainda assim, consigo realizar o meu teste.

Agora vou refazer o teste do CadastrarCliente, feito no 2º capítulo. Um teste de comportamento é um pouco mais complicado, pois preciso testar quais métodos estão sendo chamados e em que ordem. Para isto vou utilizar o Expect, dentro do using(mockery.Ordered).

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);
}

No final do teste preciso validar todas estas expectativas que defini. Faço isto utilizando o método do mockery.

mockery.VerifyAllExpectationsHaveBeenMet();

O teste completo 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;

  Mockery mockery = new Mockery();
  IMailer mailerMocked = mockery.NewMock<IMailer>();
  IRepositorio<Cliente> repositorioMocked = mockery.NewMock<IRepositorio<Cliente>>();
  IValidacao validadorMocked = mockery.NewMock<IValidacao>();

  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);
  }
    
  //Cria o servico e injeta os mocks
  ServicoCliente servico = new ServicoCliente();
  servico.Mailer = mailerMocked;
  servico.Repositorio = repositorioMocked;
  servico.Validador = validadorMocked;

  servico.CadastrarCliente(cliente);

  mockery.VerifyAllExpectationsHaveBeenMet();
}

Conclusão, menos código para manutenção, testes mais legíveis e mais poderosos.

Depois que conhecemos os conceitos por trás das ferramentas de Mock o seu uso fica bem mais lógico e intuitivo.

A seguir veremos a evolução destas ferramentas e as novidades que estão surgindo na área.

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 2 and 1 and type the answer here: