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.
-
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 classeEmployeeService.
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.
-
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étodoAdd.
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) deIEmployeeRepositorypara testar o comportamento daEmployeeServicesem interagir com a camada de dados real.
-
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
IEmployeeRepositorye verificar se os métodos corretos foram chamados.
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
}
}-
Arrange: Configuramos o cenário do teste.
- Criamos um mock de
IEmployeeRepositoryusando Moq. - Criamos uma instância do
EmployeeServicepassando o mock do repositório. - Inicializamos um
AddEmployeeInputModelcom dados de teste. - Convertemos o modelo de entrada em uma entidade esperada.
- Criamos um mock de
-
Act: Executamos o método
AdddoEmployeeService. -
Assert: Verificamos se o método
Adddo repositório foi chamado uma vez com a entidade esperada.- Usamos
mockRepository.Verifypara 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.
- Usamos
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.
- 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:
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.");
}
}
}- Usings Iniciais: Importações de namespaces necessários para o teste, incluindo
AutoFixturepara criação de dados aleatórios, definições de modelos (AddEmployeeInputModel), serviços (EmployeeService), entidades (Employee), exceções (BirthDateCannotBeInTheFutureException) e repositórios (IEmployeeRepository).
- Namespace
HrManager.UnitTests.Application.Services.EmployeeServiceTests: A estrutura do namespace sugere que esses testes estão no contexto de testes para a classeEmployeeServiceno projetoApplication.Servicesdentro do projetoHrManager. - Classe
EmployeeServiceAddTests: Classe de testes para testar o métodoAddda classeEmployeeService.
-
ValidEmployee_AddIsCalled_ReturnValidEmployeeViewModel:- Arrange: Preparação para o teste. Utiliza o
AutoFixturepara criar umAddEmployeeInputModelcom dados aleatórios. - Cria um mock de
IEmployeeRepositorye o injeta noEmployeeService. - Modifica a data de nascimento (
BirthDate) para garantir que não seja no futuro. - Act: Chama o método
AdddoEmployeeServicecom o modelo de entrada criado. - Assert: Verifica se o método
Adddo repositório foi chamado uma vez com qualquer instância deEmployee. 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.).
- Arrange: Preparação para o teste. Utiliza o
-
InvalidBirthDateForEmployee_AddIsCalled_ThrowAnInvalidBirthDateException:- Arrange: Prepara um
AddEmployeeInputModelcom uma data de nascimento (BirthDate) no futuro. - Cria um mock de
IEmployeeRepositorye o injeta noEmployeeService. - Act + Assert: Verifica se chamar o método
AdddoEmployeeServicecom um modelo de entrada inválido resulta em uma exceção do tipoBirthDateCannotBeInTheFutureException. Usa assertivas do xUnit (Assert.Throws) e Shouldly (Should.Throw) para validar a exceção e sua mensagem.
- Arrange: Prepara um
- 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.
- xUnit (
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.