ASP.NET Core - Testes com NUnit e EF Core in Memory - II
Neste artigo veremos como realizar testes de unidade para operações CRUD em uma aplicação ASP.NET Core Web API usando o Unit e o EF Core in Memory. |
Criando o projeto de Testes de unidade com Unit e EF Core in Memory
Para realizar os testes de unidade vamos usar o NUnit e o Entity Framework Core in Memory.
O NUnit é um popular framework de testes de unidade para a plataforma .NET. Ele fornece uma ampla gama de recursos e funcionalidades para facilitar a criação e execução de testes automatizados. Aqui estão os principais recursos e funções do NUnit:
O Entity Framework In-Memory é uma opção fornecida pelo Entity Framework, uma tecnologia de mapeamento objeto-relacional (ORM) da plataforma .NET, que permite a execução de testes de integração sem a necessidade de um banco de dados real. Em vez disso, ele cria um banco de dados em memória, que é usado exclusivamente para fins de teste.
O Entity Framework In-Memory funciona criando um banco de dados em memória que simula as funcionalidades e comportamentos de um banco de dados real. Ele mantém uma estrutura de dados na memória do computador, permitindo que você execute operações de consulta, inserção, atualização e exclusão como faria em um banco de dados tradicional.
A sua principal vantagem é a velocidade e a simplicidade dos testes de integração. Em vez de depender de um banco de dados real, você pode usar o banco de dados em memória para executar os testes, eliminando a necessidade de configuração e comunicação com um servidor de banco de dados.
Ao utilizar teste recurso, você pode criar um contexto de banco de dados em memória para cada teste, preencher o banco de dados com dados de teste e executar as operações que deseja testar. Após a execução dos testes, o banco de dados em memória é descartado, não afetando o estado de outros testes ou do ambiente de desenvolvimento.
Ao utilizar o Entity Framework Core In-Memory como uma substituição do banco de dados real, estamos isolando o código do banco de dados e poderemos testar apenas a lógica da unidade.
Criando o projeto de Testes
Agora podemos incluir o projeto de testes na nossa solução selecionando a opção File-> New Project e usando o template NUnit Test Project.
Vamos criar o projeto com o nome ApiBlog.Test e a seguir incluir uma referência neste projeto ao projeto ApiBlog usando a opção Add Project Reference e na sequência vamos incluir no projeto os seguintes pacotes nuget:
Você pode usar o comando Install-Package <pacote> ou dotnet add package <pacote>.
A seguir vamos criar no projeto de testes as seguintes pastas de forma a organizar o código:
Ao final a estrutura do projeto de testes deverá ser a seguinte:
Iniciando os testes de unidade
Vamos criar na pasta Controllers a classe PostsControllerTest onde vamos inicialmente configurar o contexto do EF Core :
using ApiBlog.Repositories;
using ApiBlog.Test.Helpers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace ApiBlog.Test.Controllers;
public class PostsControllerTest
{
private static DbContextOptions<AppDbContext> dbContextOptions =
new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: "BlogDb")
.Options;
AppDbContext context;
private PostRepository _postRepository;
[OneTimeSetUp]
public void Setup()
{
context = new AppDbContext(dbContextOptions);
context.Database.EnsureCreated();
SeedDataInitial.Seed(context);
_postRepository = new PostRepository(context);
}
[OneTimeTearDown]
public void CleanUp()
{
context.Database.EnsureDeleted();
}
}
|
Este código usa o NUnit e o EF Core In-Memory para configurar o ambiente e testar os endpoints de da API ApiBlog. Vamos entender o código:
Precisamos criar a classe SeedDataInitial que fornece os dados iniciais em memória para realizar os testes. Assim na pasta Helpers vamos criar a classe SeedDataInitial com o código abaixo:
public class SeedDataInitial
{
public static void Seed(AppDbContext context)
{
context.Categorias.AddRange(
new Categoria() { Nome = "CSHARP", Chave = "csharp" },
new Categoria() { Nome = "VISUAL STUDIO", Chave = "visualstudio" },
new Categoria() { Nome = "ASP.NET CORE", Chave = "aspnetcore" },
new Categoria() { Nome = "SQL SERVER", Chave = "sqlserver" }
);
context.Posts.AddRange(
new Post() { Titulo = "ASP.NET Core - CRUD", Descricao = "CRUD com EF Core ", CategoriaId = 1, DataCriacao = DateTime.Now },
new Post() { Titulo = "C# - Novidades ", Descricao = "Novidades do C# 10", CategoriaId = 3, DataCriacao = DateTime.Now },
new Post() { Titulo = "SQL Server - Funcões básicas", Descricao = "As funções básicas do SQL Server" ,CategoriaId = 4, DataCriacao = DateTime.Now },
new Post() { Titulo = "Visual Studio 2022 - Novos recursos", Descricao = "Os novos recursos do VS 2022", CategoriaId = 2, DataCriacao = DateTime.Now }
);
context.SaveChanges();
}
}
|
Agora podemos iniciar criando os métodos de testes usando o NUnit.
Vamos iniciar testando o retorno de um Post pelo seu Id considerando 3 cenários:
Abaixo temos os 3 métodos de testes para estes cenários :
[Test] public async Task Task_GetPostById_Return_OkResult() { // Arrange var controller = new PostsController(_postRepository);
// Act
// Assert }
[Test]
// Act
// Assert
[Test]
// Act
// Assert |
Entendendo o código:
No método Task_GetPostById_Return_OkResult(), podemos chamar o método GetPost() do PostsController passando um ID válido do post que você deseja buscar. Em seguida, podemos verificar o tipo de retorno e assegurar que o objeto retornado seja do tipo PostDto com o ID esperado.
Certifique-se de adaptar o ID do post que você deseja buscar e adicionar asserções adicionais conforme necessário para verificar outros atributos do PostDto.
Essas etapas devem permitir testar o retorno de um post pelo seu ID usando o NUnit e o EF Core In-Memory. Lembre-se de que já configuramos o contexto do banco de dados em memória no método de configuração Setup() da classe de teste e inseriu dados de teste usando o método SeedDataInitial.Seed(context).
No método Task_GetPostById_Return_NotFoundResult() para testar o cenário em que o post não é encontrado e o retorno é "Not Found", você pode usar o método Assert.IsInstanceOf() do NUnit para verificar se o tipo de retorno é NotFoundResult. Além disso, você também pode verificar o código de status HTTP da resposta para garantir que seja 404 (Not Found).
Para testar o cenário em que o controlador retorna um BadRequest (400), podemos usar o método Assert.IsInstanceOf() do NUnit para verificar se o tipo de retorno é BadRequestResult. Além disso, podemos verificar o código de status HTTP da resposta para garantir que seja 400 (BadRequest).
Abaixo vemos a janela do Test Explorer exibindo o resultado da execução destes testes:
A seguir temos o método de testes para o método Action do controlador que retorna todos os Posts:
[Test] public async Task Task_GetPosts_MatchResult() { // Arrange var controller = new PostsController(_postRepository);
// Act
// Assert
var okResult =
data.Should().BeOfType<OkObjectResult>().Subject;
Assert.That(posts[0].Titulo, Is.EqualTo("ASP.NET Core -
CRUD"));
Assert.That(posts[1].Titulo, Is.EqualTo("C# - Novidades ")); |
Entendendo o código acima:
Essas asserções são usadas para verificar se os resultados obtidos do método GetPosts estão corretos, comparando os títulos e descrições dos posts retornados.
Agora vamos criar o método de teste para testar a criação de um novo Post usando o método Action addPost do controlador:
[Test] public async Task Task_Add_ValidData_Return_OkResult() { //Arrange var controller = new PostsController(_postRepository); var post = new Post() { Titulo = "Teste Titulo 3", Descricao = "Teste Descrição 3", CategoriaId = 2, DataCriacao = DateTime.Now };
//Act
// Assert |
Este código atua da seguinte forma:
Agora veremos o método de teste usado para testar a atualização de um Post :
[Test]
public async Task Task_Update_ValidData_Return_OkResult()
{
// Arrange
var controller = new PostsController(_postRepository);
var postId = 2;
// Act
var existingPost = await controller.GetPost(postId);
var okResult = existingPost.Should().BeOfType<OkObjectResult>().Subject;
var result = okResult.Value.Should().BeAssignableTo<PostDto>().Subject;
var post = new Post();
post.Titulo = "Novidades do C# - Atualizado(Teste)";
post.Descricao = result.Descricao;
post.CategoriaId = result.CategoriaId;
post.DataCriacao = result.DataCriacao;
var updatedData = await controller.UpdatePost(post);
// Assert
Assert.IsInstanceOf<OkResult>(updatedData);
}
|
Vejamos o que faz este código :
Para concluir veremos o método de teste para testar a exclusão de um Post.
[Test]
public async Task Task_Delete_Post_Return_OkResult()
{
//Arrange
var controller = new PostsController(_postRepository);
var postId = 2;
//Act
var data = await controller.DeletePost(postId);
//Assert
Assert.IsInstanceOf<OkResult>(data);
// Podemos usar FuentAssertions assim
data.Should().BeOfType<OkResult>();
}
|
Aqui, estamos criando uma instância do PostsController e passando o _postRepository como dependência. Em seguida, chamamos o método DeletePost do controlador, fornecendo o ID do post que desejamos excluir.
No bloco de Assert, estamos verificando se o resultado retornado da exclusão do post é do tipo OkResult.
Usamos o método Assert.IsInstanceOf<T> para essa verificação. Também podemos usar o FluentAssertions para obter uma sintaxe mais expressiva, usando data.Should().BeOfType<OkResult>() para realizar a mesma verificação.
Concluímos assim a implementação dos testes para o CRUD usando NUnit e o EF Core in Memory com ajuda dasa FluentAssertions. Você pode expandir os métodos de teste realizando testes para outros cenários.
Pegue o projeto aqui: ApiBlog.Test.zip (sem as referências)
"Bendito seja o Deus e Pai de nosso
Senhor Jesus Cristo que, segundo a sua grande misericórdia, nos
gerou de novo para uma viva esperança, pela ressurreição de Jesus
Cristo dentre os mortos"
1 Pedro 1:3
Referências: