C# - Realizando Testes Unitários com XUnit e Moq


Neste artigo veremos como realizar testes de unidade usando o XUnit e o Moq.

A realização de testes de unidade é uma abordagem de teste de software que é executada em tempo de desenvolvimento para testar o menor componente de qualquer software.

Eu já apresentei os fundamentos do XUnit neste artigo: C# - Realizando Testes Unitários com XUnit

Hoje veremos como usar o Moq para realizar testes unitários usando o XUnit.

Existem muitos frameworks que podemos usar para mocar objetos ou realizar o mocking em nossos projetos na plataforma .NET e neste artigo eu vou tratar do framework Moq.

 

O que é Moq ?

 

Moq é uma biblioteca para mocar objetos para a plataforma .NET desenvolvida para tirar o máximo proveito de recursos como Linq, árvores de expressão, expressões lambdas, etc.

Assim, ela suporta a mocagem de interfaces bem como classes. Sua API é extremamente simples e direta, e não requer nenhum conhecimento ou experiência prévia com os conceitos de mocking.

 

A sua instalação pode se feita via Nuget ou manualmente como veremos no projeto exemplo.

Criando o projeto C#

Vamos criar um projeto do tipo Console para .NET Core no VS 2019 Community chamado CShp_UnitTestMoq;

Neste projeto vamos criar uma interface chamada ICalculadora com o seguinte código:

    public interface ICalculadora
    {
        (string operacao, double resultado) Calcular(string operacao, double a, double b);
    }

Note que o método Calcular() retorna dois valores : uma string operacao e um double resultado.

A seguir crie uma classe chamada Calculadora que implementa esta interface:

using System;
namespace CShp_UnitTestMoq
{
    public class Calculadora : ICalculadora
    {
        public (string operacao, double resultado) Calcular(string operacao, double a, double b)
        {
            (string operacao, double resultado) resultadoOperacao;
            double c;
            switch (operacao)
            {
                case "somar":
                    c = a + b;
                    break;
                case "subtrair":
                    c = a - b;
                    break;
                case "multiplicar":
                    c = a * b;
                    break;
                case "dividir":
                    c = Math.Round(a / b,2);
                    break;
                default:
                    c = a + b;
                    break;
            }
            resultadoOperacao = (operacao, c);
            return resultadoOperacao;
        }
    }
}

Temos aqui uma classe bem simples com algumas operações matemáticas que iremos usar apenas para ilustrar a utilização do XUnit com o Moq.

Vamos agora criar uma classe MaquinaCalculadora com o código a seguir:

namespace CShp_UnitTestMoq
{
    public class MaquinaCalculadora
    {
        private ICalculadora calc;
        public MaquinaCalculadora() : this(new Calculadora())
        {}
        public MaquinaCalculadora(ICalculadora obj)
        {
            this.calc = obj;
        }
        public (string operacao, double resultado) Calcular(string tipoOperacao, double a, double b)
        {
            return calc.Calcular(tipoOperacao, a, b);
        }
    }
}

Criando o projeto de Teste

Vamos agora criar o projeto de teste na mesma solução.

No menu File selecione Add -> New Project;

A seguir selecione :

Escolha o template xUnit Test Project (.NET Core) e clique em Next :

A seguir informe o nome CShp_XUnitTest clique em Create.

Vamos incluir uma referência ao projeto Cshp_Calculos.

Clique com o botão direito do mouse sobre o projeto CShp_XUnitTest e a seguir clique em Add Reference;

Na janela Reference Manager marque o projeto CShp_UnitTestMoqe clique em OK.

Agora vamos incluir uma referência ao Moq no projeto de testes via menu Tools -> Manage Nuget Packages for Solution;

Ao final teremos a nossa solução contendo os dois projetos.

Criando os testes de unidade

Vamos aproveitar a classe UnitTest1 e criar os casos de testes iniciando com o teste de unidade para o método Somar.

Crie na classe CalculoUnitTest o código a seguir que representa nosso primeiro caso de teste de unidade:

Neste caso de teste de unidade estamos somando os números 3.2 e 4.5 e esperamos o resultado igual a 7.7.

Estamos criando um objeto mock usando a interface ICalculadora e a seguir especificando uma configuração para invocar a operação 'somar' esperando o retorno igual a 7.7.

Depois criamos uma instância da classe MaquinaCalculadora() passando o objeto mocado e os números para realizar a operação.

Ao final definimos o que esperamos obter com a operação e resultado para indicar que o teste passou.

Agora no menu Test clique em Windows->Test Explorer para abrir a janela do Test Explorer:

Temos a janela exibindo no lado esquerdo o nosso teste de unidade e acima um menu com opções que permite controlar a execução dos testes. No lado direito temos os detalhes do teste selecionado.

Nesta janela iremos executar e acompanhar a execução dos testes de unidade.

Conforme você executa, grava e executa novamente os testes, o Test Explorer exibe os resultados nos grupos padrão de Testes com Falha, Testes Aprovados, Testes Ignorados e Testes Não Executados. Você pode também alterar a forma como o Test Explorer agrupa seus testes.

O painel de detalhes de teste exibe as seguintes informações:

Se o teste falhar, o painel de detalhes também exibe:

Para o nosso teste temos que o teste passou e a sua duração.

Vamos agora definir os casos de testes para as demais operações:

using CShp_UnitTestMoq;
using Moq;
using Xunit;
namespace CShp_XUnitTest
{
    public class UnitTest1
    {
        [Fact]
        public void Somar_Dois_Numeros()
        {
            // Arrange
            Moq.Mock<ICalculadora> mock = new Moq.Mock<ICalculadora>();
            mock.Setup(x => x.Calcular(It.IsAny<string>(), It.IsAny<double>(), It.IsAny<double>())).Returns(("somar", 7.7));
            MaquinaCalculadora maqCalc = new MaquinaCalculadora(mock.Object);
            // Act
            (string operacao, double resultado) op = maqCalc.Calcular("somar", 3.2, 4.5);
            // Assert
            Assert.Equal("somar", op.operacao);
            Assert.Equal(7.7, op.resultado);
        }
        [Fact]
        public void Subtrair_Dois_Numeros()
        {
            // Arrange
            Moq.Mock<ICalculadora> mock = new Moq.Mock<ICalculadora>();
            mock.Setup(x => x.Calcular(It.IsAny<string>(), It.IsAny<double>(), It.IsAny<double>())).Returns(("subtrair", -1.3));
            MaquinaCalculadora maqCalc = new MaquinaCalculadora(mock.Object);
            // Act
            (string operacao, double resultado) op = maqCalc.Calcular("subtrair", 3.2, 4.5);
            // Assert
            Assert.Equal("subtrair", op.operacao);
            Assert.Equal(-1.3, op.resultado);
        }
        [Fact]
        public void Multiplicar_Dois_Numeros()
        {
            // Arrange
            Moq.Mock<ICalculadora> mock = new Moq.Mock<ICalculadora>();
            mock.Setup(x => x.Calcular(It.IsAny<string>(), It.IsAny<double>(), It.IsAny<double>())).Returns(("multiplicar", 14.4));
            MaquinaCalculadora maqCalc = new MaquinaCalculadora(mock.Object);
            // Act
            (string operacao, double resultado) op = maqCalc.Calcular("multiplicar", 3.2, 4.5);
            // Assert
            Assert.Equal("multiplicar", op.operacao);
            Assert.Equal(14.4, op.resultado);
        }
        [Fact]
        public void Dividir_Dois_Numeros()
        {
            // Arrange
            Moq.Mock<ICalculadora> mock = new Moq.Mock<ICalculadora>();
            mock.Setup(x => x.Calcular(It.IsAny<string>(), It.IsAny<double>(), It.IsAny<double>())).Returns(("dividir", 0.71));
            MaquinaCalculadora maqCalc = new MaquinaCalculadora(mock.Object);
            // Act
            (string operacao, double resultado) op = maqCalc.Calcular("multiplicar", 3.2, 4.5); 
            // Assert
            Assert.Equal("dividir", op.operacao);
            Assert.Equal(0.71, op.resultado);
        }
    }
}

Definimos 4 casos de testes sendo que um já foi executado. Abrindo o Teste Explorer e executando todos os testes iremos obter o seguinte resultado:

Vemos que todos os testes passaram. Você pode continuar definindo outros casos de testes mas o roteiro básico é o que apresentamos acima.

Temos assim uma apresentação dos principais recursos do XUnit para realização de testes de unidade usando o Moq no .NET Core.

Pegue o código do projeto aqui:  CShp_UnitTestMoq.zip

"Tendo sido, pois, justificados pela fé, temos paz com Deus, por nosso Senhor Jesus Cristo;
Pelo qual também temos entrada pela fé a esta graça, na qual estamos firmes, e nos gloriamos na esperança da glória de Deus."

Romanos 5:1,2

Referências:


José Carlos Macoratti