diff --git a/README.md b/README.md index 311bd2a..010899b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -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. +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 diff --git a/README.md.bak b/README.md.bak new file mode 100644 index 0000000..010899b --- /dev/null +++ b/README.md.bak @@ -0,0 +1,206 @@ +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`. + + ```csharp + 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`. + + ```csharp + 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: + +```csharp +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(); + 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(e => e == expectedEmployee)), Times.Once); + Assert.NotNull(result); + Assert.IsType(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 + +```csharp +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(); + + var employeeRepositoryMock = new Mock(); + + 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()), Times.Once); + } + + [Fact] + public void InvalidBirthDateForEmployee_AddIsCalled_ThrowAnInvalidBirthDateException() + { + // Arrange + var addEmploymentInputModel = new Fixture().Create(); + addEmploymentInputModel.BirthDate = DateTime.Today.AddDays(1); + + var employeeRepositoryMock = new Mock(); + var employeeService = new EmployeeService(employeeRepositoryMock.Object); + + // Act + Assert + var exception = Assert.Throws(() => + employeeService.Add(addEmploymentInputModel)); + + Assert.Equal("Birth date cannot be in the future.", exception.Message); + + Should.Throw(() => + 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`)**: 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.