Skip to content

Testes Unitários com .NET 6, xUnit, Moq, AutoFixture e Shouldly

Notifications You must be signed in to change notification settings

DiegoLins10/HrManager

Repository files navigation

Para entender os conceitos utilizados neste código em relação a testes unitários, precisamos focar em alguns princípios chave e práticas que facilitam a testabilidade do código. Vou abordar os conceitos principais utilizados neste código e como eles se relacionam com testes unitários.

Conceitos Utilizados no Código

  1. Injeção de Dependência (Dependency Injection):

    • Definição: É um padrão de design que permite a injeção de dependências (objetos que uma classe precisa para funcionar) em uma classe através do seu construtor ou métodos.
    • No código: O repositório IEmployeeRepository é injetado no construtor da classe EmployeeService.
    public EmployeeService(IEmployeeRepository repository)
    {
        _repository = repository;
    }
    • Para testes unitários: A injeção de dependência facilita a substituição de dependências reais por mocks ou stubs durante os testes, permitindo isolar a unidade de código que está sendo testada.
  2. Interface:

    • Definição: Interfaces são contratos que definem quais métodos e propriedades uma classe deve implementar, sem fornecer a implementação real.
    • No código: IEmployeeRepository é uma interface que define o método Add.
    public interface IEmployeeRepository
    {
        void Add(Employee employee);
    }
    • Para testes unitários: O uso de interfaces permite a criação de mocks. Você pode criar uma implementação falsa (mock) de IEmployeeRepository para testar o comportamento da EmployeeService sem interagir com a camada de dados real.
  3. Mocking:

    • Definição: É o processo de criação de objetos falsos que imitam o comportamento de objetos reais. Mocks são usados para testar a interação entre unidades de código.
    • Para testes unitários: Em testes, você pode usar bibliotecas de mocking (como Moq em C#) para criar um mock de IEmployeeRepository e verificar se os métodos corretos foram chamados.

Exemplo de Teste Unitário com Moq

Aqui está um exemplo de como você poderia escrever um teste unitário para o método Add da EmployeeService usando a biblioteca Moq:

using Moq;
using Xunit;
using HrManager.Application.Models.InputModels;
using HrManager.Application.Models.ViewModels;
using HrManager.Application.Services;
using HrManager.Core.Repositories;

public class EmployeeServiceTests
{
    [Fact]
    public void Add_Should_Call_Repository_Add_And_Return_ViewModel()
    {
        // Arrange
        var mockRepository = new Mock<IEmployeeRepository>();
        var service = new EmployeeService(mockRepository.Object);
        var inputModel = new AddEmployeeInputModel
        {
            // Inicialize o modelo de entrada com dados de teste
        };
        var expectedEmployee = inputModel.ToEntity();

        // Act
        var result = service.Add(inputModel);

        // Assert
        mockRepository.Verify(r => r.Add(It.Is<Employee>(e => e == expectedEmployee)), Times.Once);
        Assert.NotNull(result);
        Assert.IsType<AddEmployeeViewModel>(result);
        Assert.Equal(expectedEmployee.Name, result.Name); // Exemplo de verificação de propriedade
    }
}

Explicação do Teste Unitário

  1. Arrange: Configuramos o cenário do teste.

    • Criamos um mock de IEmployeeRepository usando Moq.
    • Criamos uma instância do EmployeeService passando o mock do repositório.
    • Inicializamos um AddEmployeeInputModel com dados de teste.
    • Convertemos o modelo de entrada em uma entidade esperada.
  2. Act: Executamos o método Add do EmployeeService.

  3. Assert: Verificamos se o método Add do repositório foi chamado uma vez com a entidade esperada.

    • Usamos mockRepository.Verify para verificar a interação com o mock.
    • Checamos se o resultado não é nulo e se é do tipo correto.
    • Verificamos propriedades específicas no resultado para garantir que os dados foram processados corretamente.

Conclusão

O código utiliza injeção de dependência e interfaces para promover a separação de preocupações e facilitar o teste. Isso permite a criação de testes unitários eficazes, onde dependências externas podem ser mockadas para isolar a unidade de código que está sendo testada. O uso de bibliotecas de mocking como Moq facilita a verificação de interações e comportamento do código em testes.

Tecnologias

  • XUnit
  • Moq (Mock)
  • Auto fixture
  • Shouldly

Esse código é um exemplo de testes unitários usando a biblioteca xUnit.net, Moq para criação de mocks, AutoFixture para geração de dados aleatórios e Shouldly para assertivas de teste mais expressivas. Vamos analisar cada parte do código:

Estrutura Geral

using AutoFixture;
using HrManager.Application.Models.InputModels;
using HrManager.Application.Services;
using HrManager.Core.Entities;
using HrManager.Core.Exceptions;
using HrManager.Core.Repositories;
using Moq;
using Shouldly;
using System;
using Xunit;

namespace HrManager.UnitTests.Application.Services.EmployeeServiceTests
{
    public class EmployeeServiceAddTests
    {
        [Fact]
        public void ValidEmployee_AddIsCalled_ReturnValidEmployeeViewModel()
        {
            // Arrange
            var addEmploymentInputModel = new Fixture().Create<AddEmployeeInputModel>();

            var employeeRepositoryMock = new Mock<IEmployeeRepository>();

            var employeeService = new EmployeeService(employeeRepositoryMock.Object);

            // Act
            var result = employeeService.Add(addEmploymentInputModel);

            // Assert
            Assert.Equal(addEmploymentInputModel.Role, result.Role);
            Assert.Equal(addEmploymentInputModel.FullName, result.FullName);
            Assert.Equal(addEmploymentInputModel.Document, result.Document);
            Assert.Equal(addEmploymentInputModel.BirthDate, result.BirthDate);
            Assert.Equal(addEmploymentInputModel.RoleLevel, result.RoleLevel);
            Assert.Equal(addEmploymentInputModel.Role, result.Role);

            result.FullName.ShouldBe(addEmploymentInputModel.FullName);
            result.Document.ShouldBe(addEmploymentInputModel.Document);
            result.BirthDate.ShouldBe(addEmploymentInputModel.BirthDate);
            result.RoleLevel.ShouldBe(addEmploymentInputModel.RoleLevel);
            result.Role.ShouldBe(addEmploymentInputModel.Role);

            employeeRepositoryMock.Verify(er => er.Add(It.IsAny<Employee>()), Times.Once);
        }

        [Fact]
        public void InvalidBirthDateForEmployee_AddIsCalled_ThrowAnInvalidBirthDateException()
        {
            // Arrange
            var addEmploymentInputModel = new Fixture().Create<AddEmployeeInputModel>();
            addEmploymentInputModel.BirthDate = DateTime.Today.AddDays(1);

            var employeeRepositoryMock = new Mock<IEmployeeRepository>();
            var employeeService = new EmployeeService(employeeRepositoryMock.Object);

            // Act + Assert
            var exception = Assert.Throws<BirthDateCannotBeInTheFutureException>(() =>
                employeeService.Add(addEmploymentInputModel));

            Assert.Equal("Birth date cannot be in the future.", exception.Message);

            Should.Throw<BirthDateCannotBeInTheFutureException>(() =>
                employeeService.Add(addEmploymentInputModel))
                .Message.ShouldBe("Birth date cannot be in the future.");
        }
    }
}

Explicação

Usings

  • Usings Iniciais: Importações de namespaces necessários para o teste, incluindo AutoFixture para criação de dados aleatórios, definições de modelos (AddEmployeeInputModel), serviços (EmployeeService), entidades (Employee), exceções (BirthDateCannotBeInTheFutureException) e repositórios (IEmployeeRepository).

Namespace e Classe de Teste

  • Namespace HrManager.UnitTests.Application.Services.EmployeeServiceTests: A estrutura do namespace sugere que esses testes estão no contexto de testes para a classe EmployeeService no projeto Application.Services dentro do projeto HrManager.
  • Classe EmployeeServiceAddTests: Classe de testes para testar o método Add da classe EmployeeService.

Métodos de Teste

  1. ValidEmployee_AddIsCalled_ReturnValidEmployeeViewModel:

    • Arrange: Preparação para o teste. Utiliza o AutoFixture para criar um AddEmployeeInputModel com dados aleatórios.
    • Cria um mock de IEmployeeRepository e o injeta no EmployeeService.
    • Modifica a data de nascimento (BirthDate) para garantir que não seja no futuro.
    • Act: Chama o método Add do EmployeeService com o modelo de entrada criado.
    • Assert: Verifica se o método Add do repositório foi chamado uma vez com qualquer instância de Employee. Compara as propriedades do resultado (result) com as propriedades do modelo de entrada (addEmploymentInputModel) usando assertivas do xUnit (Assert.Equal) e Shouldly (result.FullName.ShouldBe, etc.).
  2. InvalidBirthDateForEmployee_AddIsCalled_ThrowAnInvalidBirthDateException:

    • Arrange: Prepara um AddEmployeeInputModel com uma data de nascimento (BirthDate) no futuro.
    • Cria um mock de IEmployeeRepository e o injeta no EmployeeService.
    • Act + Assert: Verifica se chamar o método Add do EmployeeService com um modelo de entrada inválido resulta em uma exceção do tipo BirthDateCannotBeInTheFutureException. Usa assertivas do xUnit (Assert.Throws) e Shouldly (Should.Throw) para validar a exceção e sua mensagem.

Considerações Finais

  • Mocking (Mock<IEmployeeRepository>): Usado para simular o comportamento do repositório de empregados sem realmente acessar o banco de dados.
  • AutoFixture: Utilizado para criar dados aleatórios, facilitando a preparação de cenários de teste.
  • Assertivas:
    • xUnit (Assert.Equal, Assert.Throws): Assertivas padrão do xUnit para verificar resultados.
    • Shouldly (ShouldBe, Should.Throw): Lib adicional para assertivas mais expressivas e legíveis.

Este código exemplifica uma abordagem típica de teste unitário em C#, onde cada método de teste é focado em validar um cenário específico e verificar o comportamento esperado da classe EmployeeService ao adicionar empregados.

About

Testes Unitários com .NET 6, xUnit, Moq, AutoFixture e Shouldly

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages