Ответ
Да, мокирование DbContext — стандартная практика для изоляции модульных тестов от реальной базы данных. Это позволяет тестировать бизнес-логику быстро и детерминировано.
Пример мокирования DbSet с использованием библиотеки Moq:
[Fact]
public void GetCustomer_ReturnsCustomer_WhenExists()
{
// 1. Arrange (Подготовка)
var testData = new List<Customer>
{
new Customer { Id = 1, Name = "Alice" },
new Customer { Id = 2, Name = "Bob" }
}.AsQueryable();
// Мокируем DbSet<Customer>, чтобы он вел себя как коллекция в памяти
var mockDbSet = new Mock<DbSet<Customer>>();
mockDbSet.As<IQueryable<Customer>>().Setup(m => m.Provider).Returns(testData.Provider);
mockDbSet.As<IQueryable<Customer>>().Setup(m => m.Expression).Returns(testData.Expression);
mockDbSet.As<IQueryable<Customer>>().Setup(m => m.ElementType).Returns(testData.ElementType);
mockDbSet.As<IQueryable<Customer>>().Setup(m => m.GetEnumerator()).Returns(testData.GetEnumerator());
// 2. Мокируем сам DbContext
var mockContext = new Mock<ApplicationDbContext>();
mockContext.Setup(c => c.Customers).Returns(mockDbSet.Object);
// 3. Создаем тестируемый сервис, передавая мок контекста
var service = new CustomerService(mockContext.Object);
// 4. Act (Действие) & Assert (Проверка)
var result = service.GetCustomerById(1);
Assert.NotNull(result);
Assert.Equal("Alice", result.Name);
}
Альтернативы и нюансы:
- In-Memory Database (EF Core): Для тестов, требующих более сложных запросов (с
.Include(),.ThenInclude()), иногда проще использовать поставщикMicrosoft.EntityFrameworkCore.InMemory. Важно: Его поведение не полностью идентично реальной СУБД. - Библиотеки-помощники: Рассмотрите использование библиотек, упрощающих мокирование, например,
EntityFrameworkCore.Testing.Moq. - Интеграционные тесты: Для проверки корректности SQL-запросов и миграций пишите отдельные интеграционные тесты против тестовой БД (например, SQLite в памяти или локальный Docker-контейнер).
Главный принцип: Мокируйте зависимости, чтобы тестировать свой код, а не работу Entity Framework или базы данных.