.NET - Apresentando e usando o Rhino Mocks (simulação e testes)


 Esse artigo vou apresentar os conceitos básicos sobre o Rhino Mocks e como utilizar alguns de seus recursos.


O Rhino Mocks pode ser visto como um framework dinâmico para simulação de objetos para plataforma .Net. Seu objetivo é facilitar os testes, permitindo ao desenvolvedor criar implementações de simulação de objetos personalizados e verificar as interações usando testes de unidade.

 

Existe na língua portuguesa o verbo mocar que significa enganar, atraiçoar ou ainda esconder (popular), mas na área de software mocar objetos ou Mock Objects significa objetos que imitam objetos reais para realização de testes de software.

 

Assim os mock objetos seria objetos criados usando frameworks com o objetivo de simular objetos reais e facilitar a sua criação quando da realização de testes.

 

O Rhino Mocks é um destes frameworks muito popular para plataforma .NET

 

Mas porque alguém iria precisar mocar objetos usando um framework ?

 

Se você quiser praticar o Test-Driven Development (TDD), então não tem como evitar o uso de um Framework para mocar objetos. Você vai precisa usar estes frameworks para realizar uma simulação e criar testes de unidade eficazes.

 

Test Driven Development (TDD) ou em português Desenvolvimento guiado por testes é uma técnica de desenvolvimento de software que baseia em um ciclo curto de repetições:
Primeiramente o desenvolvedor escreve um caso de teste automatizado que define uma melhoria desejada ou uma nova funcionalidade.

Então, é produzido código que possa ser  validado pelo teste para posteriormente o código ser refatorado para um código sob padrões aceitáveis. Kent Beck, considerado o criador ou o 'descobridor' da técnica, declarou em 2003 que TDD encoraja designs de código simples e inspira confiança1.

O Desenvolvimento dirigido por testes é relacionado a conceitos de programação de Extreme Programming, iniciado em 1999,2 mas recentemente tem-se criado maior interesse pela mesma em função de seus próprios ideais.3 Através de TDD, programadores podem aplicar o conceito de melhorar e depurar código legado desenvolvido a partir de técnicas antigas.
fonte: http://pt.wikipedia.org/wiki/Test_Driven_Development

 

Os testes unitários permite testar uma única unidade de código de forma isolada. Normalmente, uma unidade de código corresponde a um único método em sua aplicação. Portanto, um teste de unidade deve permitir-lhe testar apenas o código dentro de um método particular e nada mais.

 

Um teste unitário, para fins de Test-Driven Development, possui requisitos adicionais e um deles é que ele deve ser executado muito rapidamente e não necessitar de qualquer configuração do aplicativo.

 

Ao praticar o Test-Driven Development, você executa todos os testes sempre que você faz uma alteração de código. Muito provavelmente, você vai executar esses testes centenas de vezes durante a criação da aplicação. Se os seus testes de unidade forem lentos ou se eles derem trabalho para configurar o ambiente de desenvolvimento , esses testes não serão nada práticos.

 

Muito prazer SUT !

 

Antes de continuar tenho que apresentar a você o SUT.

 

Mas o que é SUT ?

 

A sigla SUT significa System Under Test (sistema em teste) e refere-se ao sistema o qual estamos testando.

 

Verificando estado e comportamento

 

Um Framework para mocar objetos como o Rhino Mocks pode ser usado para executar dois tipos diferentes de testes de unidade :

  1. Verificação de estado;

  2. Verificação de comportamento.(Esta distinção costumava ser chamado verificação de estado e o teste baseado em interação)

Ao realizar a verificação de estado, a declaração final em seu teste de unidade é tipicamente um comando assert que afirma que alguma condição é verdadeira.

Na verificação de comportamento (um teste de interação), por outro lado, você está interessado em verificar como um conjunto de objetos se comportam e interagem de uma forma particular.

 

Stubs não são Mocks ( "Mocks Aren’t Stubs" - Martin Fowler )

 

Podemos ainda usar o Rhino Mocks para criar stubs que são objetos para auxiliar no testes de ambientes.

Há várias razões que você pode criar um stub. Por exemplo, objeto real pode acessar um recurso externo, tal como um banco de dados ou o sistema de arquivo fazendo com que o objeto atual se torne muito lento para ser utilizado em um teste unitário.

 

O stub permite que você execute um teste em uma quantidade razoável de tempo, sem necessidade de qualquer configuração.

 

Então temos que :

Ou seja os Stubs são usados para representar e testar o estado de um objeto e os Mocks são usados para testar as suas interações.

 

Usando o Rhino Mocks

 

Vamos agora criar um projeto usando o Visual Studio Community 2013 usando a linguagem Visual C# e o template Console Application com o nome Usando_RhinoMocks :

 

 

A seguir vamos incluir uma classe chamada ProdutoBase  neste projeto com o seguinte código

 

namespace Usando_RhinoMocks
{
    public abstract class ProdutoBase
    {
        public abstract string Nome { get; set; }
        public abstract decimal Preco { get; set; }
        public abstract void Salvar();
    }
}

 

Vamos partir da classe ProdutoBase e supor que pretendemos em algum momento no projeto criar determinados tipos de produtos que derivam dessa classe base - por exemplo, ProdutoEletronico, ProdutoEsportivo, etc., sem que até agora tenhamos criado quaisquer dessas classes concretas.

Como podemos fazer isso ??

Usando o Rhino Mocks, claro...

Usando os recursos do Rhinos Mocks podemos 'fingir' que já temos as classes criadas , ou seja, podemos mocar as classes.

Para fazer isso vamos então criar o projeto onde iremos realizar os testes e usar os recursos do Rhino Mocks.

 

Criando o projeto de testes e usando o Rhino Mocks

 

No menu FILE clique em Add -> New Project;

 

Selecione o template Visual C# -> Test e o template Unit Test Project e informe o nome RhinoMocks_Teste;

 

 

A seguir vamos incluir uma referência ao projeto Usando_RhinoMocks neste projeto.

 

Clique com o botão direito sobre o projeto RhinoMocks_Teste e a seguir clique em Add -> Reference;

 

Clique na guia Projects e marque o projeto Usando_RhinoMocks;
 

 

Vamos incluir uma referência ao Rhino Mocks neste projeto.

 

Clique com o botão direito do mouse sobre o projeto RhiMocks_Teste e a seguir clique em Manage Nuget Packages;

 

Digite RhinoMocks na caixa Search Online e a seguir selecione o pacote RhinoMocks e clique no botão Install;

 

 

Podemos também inclua uma referência ao Rhino Mocks usando o Package Manager Console no menu TOOLS e digitando o comando descrito a seguir no console:

 

Nuget Package

 

PM> Install-Package RhinoMocks -Version 3.6.1

 

 

Para realizar o teste vamos alterar o nome da classe de teste criada por padrão para RhinoMocks_Teste e incluir o seguinte código nesta classe:

 

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using Usando_RhinoMocks;
namespace RhinoMocks_Teste
{
    [TestClass]
    public class RhinoMock_Teste
    {
        [TestMethod]
        public void TesteStubProdutoAbstract()
        {
            // Configura um stub produto
            ProdutoBase produto = MockRepository.GenerateStub<ProdutoBase>();
            produto.Nome = "Teclado";
            produto.Preco = 34.50m;
            //Teste
            Assert.AreEqual(34.50m, produto.Preco);
        }
    }
}

 

Neste código estamos chamando o método estático MockRepository.GenerateStub<ProdutoBase>() para gerar um stub para a classe abstrata ProdutoBase.

 

Depois de gerar o stub podemos tratá-lo como uma classe normal e definir e ler suas propriedades.

 

A instrução Assert deverá retornar o valor True visto que o preço atribuído à propriedade Preco é igual a valor verificado: 34.50.

 

Podemos também usar o Rhino Mocks para gerar valores de retorno falsos a partir de classes e interfaces para as quais já tenhamos gerado stubs.

Imagine que você precisa testar um método que se baseia em dados de banco de dados. No entanto, você não quer acessar o banco de dados quando você executa o teste de unidade, pois o acesso a banco de dados seria muito lento.

 

Neste caso, você pode criar um método de stub que sempre retorna um conjunto de valores definidos.

 

Vamos supor que temos uma classe chamada IProdutoRepositorio que representa a nossa camada de acesso a dados e que usa a interface IProduto. Vamos definir também uma classe Produto que implementa a interface IProduto.

 

Vamos então criar essas interfaces no projeto Usando_RhinoMocks, o mesmo onde temos a classe ProdutoBase, com o seguinte código:

 

1- IProduto

 

namespace Usando_RhinoMocks
{
    public interface IProduto
    {
       string Nome { get; set; }
       decimal Preco { get; set; }
    }
}

 

2- Produto

 

using System;
namespace Usando_RhinoMocks
{
    public class Produto : IProduto
    {
        public string Nome
        {
            get {  throw new NotImplementedException(); }
            set {  throw new NotImplementedException(); }
        }
        public decimal Preco
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }
        }
    }
}

 

3- IProdutoRepositorio

 

using System.Collections.Generic;
namespace Usando_RhinoMocks
{
    public interface IProdutoRepositorio
    {
        IProduto Get(int ProdutoId);
        IEnumerable<IProduto> Seleciona();
        bool Salvar(IProduto produto);
    }
}

 

No código acima temos um método para recuperar um conjunto de registros de produtos, um método para recuperar um determinado registro de produtos, e um método para salvar um produto.
 

Vamos criar um teste unitário usando Rhino Mocks criando um stub para a interface IProdutoRepositorio e definindo a configuração do método Seleciona() do stub de forma que ele sempre retorna um conjunto de produtos mocados ou falsos.

 

Para isso vamos definir no projeto RhinoMocks_Teste, na classe RhinoMock_Teste o seguinte código :

 

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using System.Collections.Generic;
using Usando_RhinoMocks;
using System.Linq;
namespace RhinoMocks_Teste
{
    [TestClass]
    public class RhinoMock_Teste
    {
        private IEnumerable<IProduto> _fakeProdutos = new List<IProduto>
        {
            new Produto {Nome = "Carne", Preco = 9.50m},
            new Produto {Nome = "Leite", Preco = 2.30m},
            new Produto {Nome = "Ovos", Preco = 3.50m}
        };
        [TestMethod]
        public void TesteStubProdutoInterface()
        {
            MockRepository mocks = new MockRepository();
            IProdutoRepositorio produtos = mocks.Stub<IProdutoRepositorio>();
            using (mocks.Record())
            {
                SetupResult.For(produtos.Seleciona()).Return(_fakeProdutos);
            }
            var resultados = produtos.Seleciona();
            Assert.AreEqual(3, resultados.Count());
        }
    }
}


Há três diferenças importantes que você deve observar entre o código acima e o usado nos exemplos anteriores.

Primeiro, observe que a sub para a interface IProdutoRepositorio não é criado usando o método GenerateStub<T>().

Quando definir os valores de retorno do método, você deve criar uma instância da classe MockRepository e chamar o método de instância Stub.

Em segundo lugar, note que classe SetupResult é usada para configurar o valor de retorno para o método IProdutoRepositorio.Seleciona(). Quando o método Seleciona() for chamado, o conteúdo do campo _fakeProdutos será devolvido.

Finalmente, observe que a chamada para SetupResult é envolta em uma declaração que faz referência ao método MockRespository.Record().

 

A declaração using em conjunto com o método de Record() são usados ​​para registrar como o stub deve se comportar quando um método stub particular for chamado. Você pode configurar vários métodos de stub dentro da única instrução using.

Você pode até mesmo retornar valores diferentes a partir de um método de stub quando diferentes parâmetros são passados ​​para o método.

 

Vamos incluir o método TesteStubMultiploRetorno na classe RhinoMock_Teste do projeto RhinoMocks_Teste com o código abaixo:
 

    [TestMethod]
    public void TesteStubMultiploRetornos()
    {
            MockRepository mocks = new MockRepository();
            IProdutoRepositorio produtos = mocks.Stub<IProdutoRepositorio>();
            using (mocks.Record())
            {
                SetupResult
                 .For(produtos.Get(2))
                 .Return(new Produto { Nome = "Cerveja", Preco = 4.99m });
                SetupResult
                 .For(produtos.Get(12))
                  .Return(new Produto { Nome = "Carne", Preco = 12.50m });
            }
            //Teste
            IProduto produto1 = produtos.Get(2);
            Assert.AreEqual("Cerveja", produto1.Nome);
            IProduto produto2 = produtos.Get(12);
            Assert.AreEqual("Carne", produto2.Nome);
            IProduto produto3 = produtos.Get(13);
            Assert.IsNull(produto3);
   }

 

Este código retorna diferentes produtos dependendo do código do produto informado ao método ProdutoRepositorio.Get().

 

Verificando o comportamento com Rhino Mocks

 

Você pode usar o Rhino Mocks para verificar se um determinado conjunto de objetos interagem de uma maneira esperada. Por exemplo, você pode verificar se um método de um determinado objeto foi chamado o número esperado de vezes com o conjunto esperado de parâmetros. Este tipo de teste é também chamado de teste com base em interação.

Este tipo de teste é útil quando você não pode verificar o estado de um objeto depois que você interagir com o objeto. Por exemplo, um objeto pode não ter uma propriedade pública contra a qual você possa usar um Assert. Portanto, não há nenhuma maneira direta para validar o estado do objeto depois de executar o teste de unidade.

Imagine que você precisa criar um componente de log. Toda vez que você realizar uma ação significativa em seu aplicativo, você quiser registrar a ação em um arquivo de log no disco. Se o seu componente de logr não tem uma propriedade que expõe todas as suas entradas de log, não haverá nenhuma maneira simples de verificar se o log está se comportando conforme o esperado.

No entanto, você pode interceptar solicitações para o componente log e pode verificar se outros componentes estão interagindo com o componente quando você espera. A verificação de comportamento é realizar este tipo de teste da interação entre diferentes componentes.

 

Ao realizar testes de verificação de comportamento com o Rhino Mocks, seu código de teste é dividido em duas seções. Primeiro, você tem uma seção de código onde você grava as suas expectativas. Em segundo lugar, você tem uma seção de código onde as suas expectativas são testadas contra a realidade.

 

A título de exemplo vamos incluir no projeto Usando_RhinoMocks as classes Log e Cliente com o código abaixo:

 

1- Classe Log

 

namespace Usando_RhinoMocks
{
    public class Log
    {
        public int Id { get; private set; }
        public Log()
        {}
        public virtual void Logar(int id)
        {         
             throw new Exception("erro");
        }
    }
}

 

2- Classe Cliente

 

namespace Usando_RhinoMocks
{
    public class Cliente
    {
        public int Id { get; private set; }
        public string Nome { get; set; }
        private Log _log;
  
        public Cliente(int Id, Log log)
        {
           this.Id = Id;
           _log = log;
        } 
        public void Salvar()
        {
           _log.Logar(this.Id);
        }
    }
}
 

 

A seguir vamos definir a classe RhinoMocksLogTeste no projeto RhinoMocks_Teste :

 

 [TestClass]
  public class RhinoMocksLogTeste
  {
        [TestMethod]
        public void LogTeste()
        {
            MockRepository mocks = new MockRepository();
            Log _log = mocks.CreateMock<Log>();
            using (mocks.Record())
            {
                _log.Logar(27);
            }
            using (mocks.Playback())
            {
                Cliente novoCliente = new Cliente(27, _log);
                novoCliente.Nome = "Macoratti";
                novoCliente.Salvar();
            }
        }
 }

 

Este código é um exemplo de teste unitário para um componente de log que citamos acima.

 

Na primeira parte do código temos um conjunto de expectativas gravadas. Assim quando o método Cliente.Salvar() for chamado o método _log.Logar() deve ser chamado com o código a ser salvo.

 

Se você observar o código da classe Log implementada no projeto Usando_RhinoMocks verá que essa classe não foi realmente implementada visto que o método Logar() lança uma exceção e como estamos mocando o método Logar esta exceção  nunca será lançada.

 

Rhino Mocks Opções de uso

 

O Rhino Mocks suporta três tipos básicos de objetos fictícios:

1- Strict Mock :   A simulação rigorosa exige que você forneça implementações alternativas para cada método/propriedade que for usada na simulação. Se quaisquer métodos/propriedades usadas não tiver uma implementação, uma exceção será lançada.

2- Mock Dinâmico :   Com uma simulação dinâmica, quaisquer métodos/propriedades que forem chamados por seus testes e não tiver uma implementação, retornará o valor padrão para o tipo do valor de retorno de dados. Em outras palavras, você vai receber o valor 0 para tipos de números, falsos para Booleanos e nulos para quaisquer tipos de objetos.

3- Mock parcial :  A simulação parcial vai usar a implementação do objeto subjacente se você não fornecer uma implementação alternativa. Então, se você só está querendo substituir algumas das funcionalidades (ou propriedades) e manter o resto, você vai querer usar esta opção. Por exemplo, se você só quer substituir o método IsLogAtivo() e deixar o resto da classe como está, você vai querer usar uma simulação parcial para só fornecer uma implementação alternativa para IsLogAtivo().

 

Estes são alguns dos muitos recursos que você poderá usar do Rhino Mocks e é apenas uma pequena amostra do que ele pode lhe oferecer para realizar testes unitários com simulações.

 

Para se aprofundar mais sugiro que você acesse a documentação da ferramenta em : http://www.ayende.com/wiki/Rhino+Mocks+Documentation.ashx

Pegue o projeto completo aqui:  Usando_RhinoMocks.zip

Quem ama a sua vida perdê-la-á, e quem neste mundo odeia a sua vida, guardá-la-á para a vida eterna.
Se alguém me serve, siga-me, e onde eu estiver, ali estará também o meu servo. E, se alguém me servir, meu Pai o honrará.

João 12:25,26

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti