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.