EF
Core - Testes de unidade em memória
![]() |
Neste artigo veremos como realizar testes de unidade usando o EF Core em memória. |
Considerando o cenário
Para realizar os testes de unidade vamos supor uma aplicação de comércio eletrônico onde teremos objetos como : CarrinhoItem, CarrinhoRepository e CarrinhoService
Neste cenário para realizar testes de unidade para a classe CarrinhoService que depende de CarrinhoRepository geralmente teríamos que simular o repositório usando um Moq para isso.
Entretanto essa abordagem pode levar a uma configuração extensa de simulações especialmente para consultas complexas.
Além disso podemos elencar as seguintes vantagens em usar a abordagem dos testes de unidade em memória:
Testes mais Próximos do Mundo Real:
Ausência de Dependências Externas:
Integração com o EF Core:
Desta forma uma alternativa válido para realizar testes de unidade é usar o banco de dados na memória do EF Core. Essa abordagem envolve operações reais de banco de dados, mas em um banco de dados leve e na memória e fornece um ambiente de teste mais realista em comparação com a simulação
Preparando o ambiente
Vamos partir de uma aplicação ASP.NET Core ECommerce, chamada ApiECommerce - onde para simplificar vamos ter apenas o gerenciamento dos itens do carrinho de compras definido através do controlador CarrinhoController que usa uma instância de ICarrinhoRepository para realizar as operações CRUD em banco de dados SQL Server.
Assim não estamos usando um serviço e vamos realizar os testes de unidade nos endpoints do controlador CarrinhoController.
A primeira coisa a fazer é incluir um projeto de testes unitários no projeto Asp.NET Core usando o template xUnit Test Project e criando o projeto com o nome CarrinhoItemUnitTests
A seguir precisamos incluir no projeto de testes o pacote
:
Microsoft.EntityFrameworkCore.InMemory
Além disso temos que incluir no projeto de testes uma referência ao projeto ApiEcommerce usando a opção Add Project Reference
Feito isso vamos definir o seguinte código para configurar o ambiente e definir um método de teste para incluir um item no carrinho:
using ApiECommerce.Context; using ApiECommerce.Entities; using ApiECommerce.Repositories; using Microsoft.EntityFrameworkCore; namespace CarrinhoItemTests; public class CarrinhoItemUnitTests
{
private readonly DbContextOptions<AppDbContext> _dbOptions;
public CarrinhoItemUnitTests()
{
_dbOptions = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
}
[Fact]
public async Task AdicionarItemAoCarrinho_Deve_AdicionarItem()
{
// Arrange
using (var context = new AppDbContext(_dbOptions))
{
var carrinhoRepository = new CarrinhoRepository(context);
var item = new CarrinhoItem
{
Id = 99,
Nome = "Produto de Teste",
Preco = 19.99m
};
// Act
await carrinhoRepository.AddAsync(item);
// Assert
var addedItem = await carrinhoRepository.GetByIdAsync(item.Id);
Assert.NotNull(addedItem);
Assert.Equal(item.Nome, addedItem.Nome);
Assert.Equal(item.Preco, addedItem.Preco);
}
}
}
|
Entendendo o código:
Arrange: Configuramos o ambiente de teste, criamos um item
de carrinho (item) que será adicionado ao carrinho e
configuramos o repositório do carrinho para utilizar o contexto do banco de
dados em memória.
Act: Realizamos a ação que queremos testar, que é adicionar
o item ao carrinho usando o método AddAsync do repositório.
Assert: Verificamos se o item foi adicionado corretamente ao
carrinho, utilizando o método GetByIdAsync do repositório e
fazendo as asserções necessárias.
Acionando a opção Test Explorer do menu Test no Visual Studio teremos a janela Test Explorer onde executando o teste teremos o seguinte resultado:

A seguir temos o código completo usado para testar cada um dos endpoints considerando apenas um cenário de teste:
public class CarrinhoItemUnitTests
{
private readonly DbContextOptions<AppDbContext> _dbOptions; public CarrinhoItemUnitTests()
{
_dbOptions = new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
}
[Fact]
public async Task AdicionarItemAoCarrinho_Deve_AdicionarItem()
{
// Arrange
using (var context = new AppDbContext(_dbOptions))
{
var carrinhoRepository = new CarrinhoRepository(context);
var item = new CarrinhoItem
{
Id = 99,
Nome = "Produto de Teste",
Preco = 19.99m
};
// Act
await carrinhoRepository.AddAsync(item);
// Assert
var addedItem = await carrinhoRepository.GetByIdAsync(item.Id);
Assert.NotNull(addedItem);
Assert.Equal(item.Nome, addedItem.Nome);
Assert.Equal(item.Preco, addedItem.Preco);
}
}
[Fact]
public async Task ObterItemDoCarrinhoPorId_Deve_RetornarItem()
{
// Arrange
using (var context = new AppDbContext(_dbOptions))
{
var carrinhoRepository = new CarrinhoRepository(context);
var itemId = 77;
var item = new CarrinhoItem
{
Id = itemId,
Nome = "Produto de Teste",
Preco = 19.99m
};
await carrinhoRepository.AddAsync(item); // Act
var fetchedItem = await carrinhoRepository.GetByIdAsync(itemId);
// Assert
Assert.NotNull(fetchedItem);
Assert.Equal(item.Nome, fetchedItem.Nome);
Assert.Equal(item.Preco, fetchedItem.Preco);
}
}
[Fact]
public async Task ObterTodosOsItensDoCarrinho_Deve_RetornarItens()
{
// Arrange
using (var context = new AppDbContext(_dbOptions))
{
var carrinhoRepository = new CarrinhoRepository(context);
var item1 = new CarrinhoItem
{
Id = 66,
Nome = "Produto 1",
Preco = 10.0m
};
var item2 = new CarrinhoItem
{
Id = 55,
Nome = "Produto 2",
Preco = 15.0m
};
await carrinhoRepository.AddAsync(item1);
await carrinhoRepository.AddAsync(item2);
// Act
var carrinhoItens = await carrinhoRepository.GetAllAsync();
// Assert
Assert.NotNull(carrinhoItens);
// Assumindo que há 2 itens no carrinho
Assert.Equal(2, carrinhoItens.Count());
}
}
[Fact]
public async Task AtualizarItemDoCarrinho_Deve_AtualizarItem()
{
// Arrange
using (var context = new AppDbContext(_dbOptions))
{
var carrinhoRepository = new CarrinhoRepository(context);
var itemId = 44;
var item = new CarrinhoItem
{
Id = itemId,
Nome = "Produto Original",
Preco = 29.99m
};
await carrinhoRepository.AddAsync(item); var itemAtualizado = new CarrinhoItem
{
Id = itemId,
Nome = "Produto Atualizado",
Preco = 39.99m
};
// Act
await carrinhoRepository.UpdateAsync(itemAtualizado);
// Assert
var updatedItem = await carrinhoRepository.GetByIdAsync(itemId);
Assert.NotNull(updatedItem);
Assert.Equal(itemAtualizado.Nome, updatedItem.Nome);
Assert.Equal(itemAtualizado.Preco, updatedItem.Preco);
}
}
[Fact]
public async Task RemoverItemDoCarrinho_Deve_RemoverItem()
{
// Arrange
using (var context = new AppDbContext(_dbOptions))
{
var carrinhoRepository = new CarrinhoRepository(context);
var itemId = 88;
var item = new CarrinhoItem
{
Id = itemId,
Nome = "Produto a ser removido",
Preco = 49.99m
};
await carrinhoRepository.AddAsync(item); // Act
await carrinhoRepository.RemoveAsync(itemId);
// Assert
var removedItem = await carrinhoRepository.GetByIdAsync(itemId);
Assert.Null(removedItem);
}
}
}
|
E estamos conversados...
"Porque o Senhor Deus é um sol e escudo; o Senhor dará graça e glória; não
retirará bem algum aos que andam na retidão"
Salmos 84:11
Referências:
C# - Tasks x Threads. Qual a diferença
DateTime - Macoratti.net
Null o que é isso ? - Macoratti.net
Formatação de data e hora para uma cultura ...
C# - Calculando a diferença entre duas datas
NET - Padrão de Projeto - Null Object Pattern
C# - Fundamentos : Definindo DateTime como Null ...
C# - Os tipos Nullable (Tipos Anuláveis)