Motivação

A utilização de dados fictícios (mock data) é essencial no desenvolvimento de projetos, sejam eles grandes ou pequenos. Isso ocorre porque nem sempre temos, ou é interessante utilizar, bancos de dados relacionais para testes. É aí que entra o framework Moq. Ele serve para que consigamos criar dados fictícios, baseados em nosso modelo de negócios, para verificar o comportamento de nossas aplicações.

O Moq pode ser utilizado para emular tanto repositórios de dados quanto objetos individuais em testes unitários.

Saiba mais sobre os testes unitários em .NET

Passo 1: Instalando o Moq

O framework Moq pode ser adicionado a qualquer projeto .NET, e trata-se de uma excelente ferramenta. Ele é mais comumente utilizado em projetos que adotam testes de forma exaustiva, por facilitar a realização dos mesmos. Sua instalação pode ser feita através do NuGet, de duas formas:

  1. Através da interface gráfica: Para acessar os pacotes do NuGet através da interface gráfica, basta clicar com o botão direito em cima do projeto e então em “Manage NuGet Packages...”, como mostra a Figura 1.


    Figura 1. Acessando o administrador de pacotes do NuGetCom isso, uma interface será mostrada, e podemos realizar a busca pelo pacote “Moq”, como mostra a Figura 2. Então, basta clicar no botão Install, à direita, para instalar a versão estável mais recente do pacote.


    Figura 2. Buscando o pacote Moq no administrador do NuGet
  2. Através da linha de comando: utilizando o Package Manager Console (View Other Windows Package Manager Console), basta executarmos o comando abaixo:

    Install-Package Moq –project <Nome_do_projeto>

    O nome do projeto é opcional e, caso não seja especificado, o pacote será instalado no projeto atual.

Passo 2: Criando o modelo

Nesse exemplo, o modelo de negócios será representado por uma classe, que conterá os dados da entidade a ser salva. Para isso, criaremos um projeto separado, chamado “Models”, em nossa solução e adicionaremos uma classe Produto a ele, como mostra a Figura 3. A classe, com suas propriedades, pode ser vista na Listagem 1. Note que temos um identificador (Id), Nome, Descricao e Preco nessa classe.

Figura 3. Classe Produto em Models
namespace Models
{
    public class Produto
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Descricao { get; set; }
        public decimal Preco { get; set; }
    }
}
Listagem 1. Classe Produto

Passo 3: Criando o verificador de preço do Produto

No projeto Models, vamos criar um verificador de preço de nossos produtos, com três valores de retorno possíveis: “Produto barato!”, “Produto na média de preço!” e “Produto caro!”. Esse método é bastante simples e serve apenas de exemplo, mas algo assim pode ser útil em aplicações de controle de estoque, por exemplo, ao avaliar a quantidade disponível de um produto e compará-la com o mínimo sugerido para esse item.

Para que possamos utilizar o Moq, precisamos de uma interface na qual o framework se baseará para simular o comportamento a ser testado. Pensando nisso, vamos criar a interface IVerificadorPrecoProduto, que conterá a assinatura para o método de verificação do preço, como mostra a Listagem 2. Essa assinatura será utilizada pelo Moq para saber qual método será simulado e qual valor deve ser retornado no momento da sua invocação, como veremos posteriormente no Passo 4.

public interface IVerificadorPrecoProduto
{
    string VerificaPrecoProduto(Produto p);
}
Listagem 2. Interface IVerificadorPrecoProduto

Já a classe VerificadorPrecoProduto pode ser vista na Listagem 3.

namespace Models
{
    public class VerificadorPrecoProduto : IVerificadorPrecoProduto
    {
        public string VerificaPrecoProduto(Produto p)
        {
            if (p.Preco > 100)
                return "Produto caro!";
            else if (p.Preco <= 100 && p.Preco > 40)
                return "Produto na média de preço!";
            else
                return "Produto barato!";
        }
    }
}
Listagem 3. Classe VerificadorPrecoProduto

Essa classe contém apenas um método, que recebe um Produto e realiza testes com base no seu preço:

  • Caso o preço seja maior que R$100,00, retorna que é um produto caro;
  • Caso o preço seja menor ou igual a R$100,00 e maior que R$40,00, retorna que é um produto na média de preço;
  • Caso não satisfaça as condições anteriores, ou seja, o preço seja menor ou igual a R$40,00, retorna-se que é um produto barato.

Como é possível notar, o valor de retorno é uma string com a mensagem em questão.

Saiba mais sobre as interfaces em C#

Passo 4: Criando e executando um teste unitário com Moq

Primeiramente, vamos criar um projeto do tipo Unit Tests, nomeado aqui apenas como “UnitTests”. Podemos notar que o mesmo cria para nós uma classe de testes, chamada UnitTest1, que vem com um método de teste, TestMethod1(). Apenas visando a legibilidade do código, vamos alterar o nome desse método para ValidaVerificadorPrecoProduto(), cuja implementação pode ser vista na Listagem 4.

O projeto UnitTests precisa referenciar o projeto Models.

01 [TestMethod]
02 public void ValidaVerificadorPrecoProduto()
03 {
04   // arrange
05   Produto produtoBarato = new Produto()
06   {
07       Preco = 35
07   };
08   Mock<IVerificadorPrecoProduto> mock = new Mock<IVerificadorPrecoProduto>();
09   mock.Setup(m => m.VerificaPrecoProduto(produtoBarato)).Returns("Produto barato!");
10   VerificadorPrecoProduto verif = new VerificadorPrecoProduto();
11
12   // act
13   var resultadoEsperado = mock.Object.VerificaPrecoProduto(produtoBarato);
14   var resultado = verif.VerificaPrecoProduto(produtoBarato);
15
16   // assert
17   Assert.AreEqual(resultado, resultadoEsperado);
18 }
Listagem 4. Método de testes ValidaVerificadorPrecoProduto()

Linha 1: Definição do método de testes (data annotation TestMethod);

Linhas 5 a 7: Definição de produto com preço barato (menor ou igual a R$40,00);

Linha 8: Criação do objeto Mock, que utiliza a interface IVerificadorPrecoProduto, sendo, então, fortemente tipado. Vale ressaltar que o objeto Mock poderia emular qualquer implementação da interface definida, caso existisse mais de uma;

Linha 9: Realização do setup de objeto Mock, que foi definido para esperar um retorno de produto barato. Note que, independentemente do valor do preço no objeto produtoBarato, o valor de retorno do objeto Mock será sempre a string “Produto barato!”, pois assim foi definido;

Linha 10: Criação do objeto VerificadorPrecoProduto para comparação posterior;

Linha 13: Execução do método VerificaProdutoBarato() no objeto Mock;

Linha 14: Execução do método VerificaProdutoBarato() no objeto normal;

Linha 17: Seção assert do teste, que faz com que o teste passe caso os resultados sejam iguais.

O resultado da execução do teste (menu Run Tests ou CTRL+R+T) é mostrado na Figura 4. Note que as informações retornadas indicam que o teste passou. No entanto, se alterarmos, por exemplo, o preço do produto para o valor R$55,00, o teste falhará, como mostra a Figura 5. Isso ocorre porque estamos comparando um produto de preço médio, de acordo com nossas regras de negócio, com um objeto mock que imita um produto barato.

Figura 4. Testes unitários executados - sucesso Figura 5. Testes unitários executados - falha

Com isso, podemos verificar que o Moq é um excelente recurso. Além dele, o Rhino Mocks é outro que pode ser utilizado com propósito similar. O Moq também é muito útil em situações que envolvem injeção de dependências, pois facilita o teste de interfaces com dados fictícios, sem que seja necessário acoplar um banco de dados apenas para verificar o funcionamento inicial do projeto. Nesse tipo de cenário, o framework funciona muito bem em conjunto com os padrões Repository e Unit of Work.