Есть ли у вас опыт использования InMemory Database (например, в EF Core) для тестирования?

«Есть ли у вас опыт использования InMemory Database (например, в EF Core) для тестирования?» — вопрос из категории Тестирование, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, активно использовал InMemory Database (провайдер Microsoft.EntityFrameworkCore.InMemory) для модульного и интеграционного тестирования кода, работающего с EF Core.

Зачем это нужно? Для создания быстрых, изолированных тестов, которые не требуют развертывания реальной СУБД (SQL Server, PostgreSQL) и не оставляют после себя данных.

Пример юнит-теста с InMemory DbContext:

[Fact]
public async Task GetUserById_ShouldReturnCorrectUser()
{
    // 1. Arrange (Подготовка)
    var options = new DbContextOptionsBuilder<ApplicationDbContext>()
        .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) // Уникальное имя для изоляции тестов
        .Options;

    // Заполняем базу тестовыми данными
    using (var context = new ApplicationDbContext(options))
    {
        context.Users.Add(new User { Id = 1, Name = "Alice", Email = "alice@test.com" });
        context.Users.Add(new User { Id = 2, Name = "Bob", Email = "bob@test.com" });
        await context.SaveChangesAsync();
    }

    var repository = new UserRepository(new ApplicationDbContext(options));

    // 2. Act (Действие)
    var result = await repository.GetByIdAsync(1);

    // 3. Assert (Проверка)
    Assert.NotNull(result);
    Assert.Equal("Alice", result.Name);
    Assert.Equal("alice@test.com", result.Email);
}

Важные ограничения и нюансы:

  • Не полная эмуляция СУБД: InMemory провайдер не обеспечивает:
    • Референциальную целостность (ограничения внешних ключей часто игнорируются).
    • Сложные ограничения уникальности.
    • Транзакционность в полном смысле (например, Rollback).
    • Конкретное SQL-поведение (хранимые процедуры, пользовательские функции, специфичные типы данных).
  • Изоляция тестов: Важно использовать Guid.NewGuid().ToString() для имени базы, чтобы каждый тест работал с чистым состоянием.
  • Альтернативы для более точного тестирования:
    • SQLite in-memory mode: Более точно эмулирует реляционную БД, поддерживает транзакции и ограничения.
    • TestContainers: Запускает реальную СУБД (PostgreSQL, SQL Server) в Docker-контейнере — идеально для интеграционных тестов, но медленнее.

Вывод: InMemory Database — отличный инструмент для быстрых юнит-тестов логики приложения, но для тестирования сложных запросов или миграций лучше использовать более приближенные к продакшену варианты.