Como Criar Mocks do DbContext no Entity Framework Core 8 para Testes Unitários
Source: Dev.to
Introdução
Os testes unitários são essenciais no desenvolvimento de software, pois garantem a qualidade e a confiabilidade do código. No caso de aplicações que utilizam o Entity Framework Core 8, testar a lógica que interage com o banco de dados pode ser desafiador. Uma solução eficiente é criar mocks do DbContext, permitindo testar a lógica de negócio sem depender de um banco de dados real.
Por que mockar o DbContext?
- Isolamento – Permite testar a lógica de negócio separadamente, sem depender de um banco de dados real.
- Desempenho – Reduz o tempo dos testes, eliminando operações de entrada e saída (I/O).
- Confiabilidade – Garante que os testes sejam reproduzíveis e consistentes.
Mockar métodos assíncronos do EF Core, como ToListAsync ou FirstOrDefaultAsync, pode ser desafiador porque o EF Core utiliza IAsyncEnumerable e IAsyncQueryProvider para implementar essas operações. É necessário simular corretamente o comportamento dessas interfaces assíncronas.
Pré‑requisitos
| Pacote | Comando |
|---|---|
| Entity Framework Core 8 | dotnet add package Microsoft.EntityFrameworkCore |
| EF Core InMemory (opcional) | dotnet add package Microsoft.EntityFrameworkCore.InMemory |
| Moq (mocking) | dotnet add package Moq |
| xUnit (framework de testes) | dotnet add package xUnit |
| MockQueryable (simulação de métodos assíncronos) | dotnet add package MockQueryable.Moq |
Certifique‑se de que a aplicação está configurada para utilizar a versão mais recente do EF Core e que os pacotes estejam devidamente instalados.
Criando um DbSet falso com suporte a operações assíncronas
public class Produto
{
public int Id { get; set; }
public string Nome { get; set; } = string.Empty;
public decimal Preco { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet Produtos { get; set; } = null!;
public AppDbContext() { }
}
Essas classes representam um cenário comum de uma aplicação CRUD, onde manipulamos objetos de domínio como Produto através de um contexto do EF Core.
Mockando o DbContext
var dadosFalsos = new List
{
new Produto { Id = 1, Nome = "Produto A", Preco = 10.0m },
new Produto { Id = 2, Nome = "Produto B", Preco = 20.0m }
};
var mockSet = dadosFalsos.AsQueryable().BuildMockDbSet();
mockSet.Setup(x => x.FindAsync(It.IsAny()))
.ReturnsAsync((object[] input) =>
dadosFalsos.FirstOrDefault(p => p.Nome == (string)input[0]));
var mockContext = new Mock();
mockContext.Setup(c => c.Produtos).Returns(mockSet.Object);
Serviço de exemplo
public class ProdutoService
{
private readonly AppDbContext _context;
public ProdutoService(AppDbContext context)
{
_context = context;
}
public async Task ObterProdutoPorNomeAsync(string nome)
{
return await _context.Produtos.FirstOrDefaultAsync(p => p.Nome == nome);
}
}
Teste unitário do serviço
using Xunit;
using MockQueryable.Moq;
using System.Threading.Tasks;
public class ProdutoServiceTests
{
[Fact]
public async Task ObterProdutoPorNomeAsync_DeveRetornarProdutoCorreto()
{
// Arrange
var dadosFalsos = new List
{
new Produto { Id = 1, Nome = "Produto A", Preco = 10.0m },
new Produto { Id = 2, Nome = "Produto B", Preco = 20.0m }
};
var mockSet = dadosFalsos.AsQueryable().BuildMockDbSet();
mockSet.Setup(x => x.FindAsync(It.IsAny()))
.ReturnsAsync((object[] input) =>
dadosFalsos.FirstOrDefault(p => p.Nome == (string)input[0]));
var mockContext = new Mock();
mockContext.Setup(c => c.Produtos).Returns(mockSet.Object);
var service = new ProdutoService(mockContext.Object);
// Act
var resultado = await service.ObterProdutoPorNomeAsync("Produto A");
// Assert
Assert.Equal("Produto A", resultado?.Nome);
}
}
Conclusão
Mockar o DbContext com suporte a métodos assíncronos no Entity Framework Core 8 pode ser desafiador, mas é uma prática valiosa para criar testes rápidos e confiáveis. A configuração correta permite que você teste a lógica de negócio isoladamente, garantindo qualidade e eficiência no desenvolvimento. Ao dominar essas técnicas, é possível construir aplicações robustas, com código testável e confiável, essencial para um desenvolvimento ágil e sustentável.