Ответ
Да, мокирование 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 или базы данных.
Ответ 18+ 🔞
А, ну это ж классика, блядь! Мокировать DbContext — это как надевать презерватив перед тестом: защищает от неожиданных последствий и лишней возни с реальной базой. Тесты тогда летают, а не ползают, как улитка по стеклу.
Смотри, вот как это обычно делают, на примере с этим вашим 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 куклу, которая будет притворяться коллекцией
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);
}
Ну и конечно, есть нюансы, ёпта. Иногда этот Moq такой геморройный, что проще взять In-Memory базу от EF Core. Она, конечно, не настоящая — ведёт себя как сумасшедший родственник на свадьбе, но для простых сценариев сойдёт.
А если совсем заебало эту клоунаду с моками настраивать, есть библиотеки-помощники, которые делают ту же хуйню, но в один клик. Но это уже как дорогой презерватив с рёбрышками — не всем надо.
И главное, запомни: юнит-тесты — это проверить твой код, а не выебать мозги Entity Framework. Для проверки, как там твои запросы в реальной базе исполняются, есть интеграционные тесты. Вот там уже можно развернуть SQLite в памяти или даже Docker-контейнер, и проверить всё по-взрослому.
Короче, мокируй зависимости, тестируй логику, и не усложняй там, где не надо. А то потом сидишь, дебажишь тест, а он падает из-за какой-нибудь хуйни в конфиге базы. Пиздец, а не жизнь.