Ответ
Выбор подхода зависит от типа тестов. Для модульных тестов стараюсь максимально изолировать код от реальной БД, а для интеграционных — работаю с тестовыми экземплярами.
1. Модульные тесты (Изоляция)
Использую моки (Mock) или стабы (Stub) для абстракций доступа к данным (например, IRepository). Это самый быстрый и надежный способ.
Пример с Moq (C#):
[Fact]
public void GetUser_ShouldReturnUser()
{
// Arrange
var mockRepo = new Mock<IUserRepository>();
var expectedUser = new User { Id = 1, Name = "Test" };
mockRepo.Setup(repo => repo.GetById(1)).Returns(expectedUser);
var service = new UserService(mockRepo.Object);
// Act
var result = service.GetUser(1);
// Assert
Assert.Equal(expectedUser, result);
mockRepo.Verify(repo => repo.GetById(1), Times.Once); // Проверяем вызов
}
2. Интеграционные тесты (Работа с БД)
Здесь нужна реальная или приближенная к реальной СУБД.
- InMemory Provider (EF Core): Быстро, но неполноценно. Подходит только для простой проверки логики, не имитирует специфичные для БД функции (ограничения, транзакции, индексы).
var options = new DbContextOptionsBuilder<AppDbContext>() .UseInMemoryDatabase(databaseName: "TestDb") .Options; - Локальная файловая БД (SQLite): Лучшая эмуляция, особенно в режиме
in-memory. Поддерживает многие функции реальной СУБД.var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); var options = new DbContextOptionsBuilder<AppDbContext>() .UseSqlite(connection) .Options; - TestContainers (Docker): Золотой стандарт для интеграционных тестов. Запускает реальную БД (PostgreSQL, MySQL) в Docker-контейнере. Максимально приближено к production.
// Пример на Java @Container static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
3. Ключевые практики
- Изоляция тестов: Каждый тест должен работать со своим набором данных. Использую транзакции с откатом (
TransactionScope) или очистку БД перед каждым тестом. - Идемпотентность: Тесты должны давать одинаковый результат при многократном запуске.
- Отдельная тестовая БД: Никогда не тестирую на production или shared-базе. Использую отдельный инстанс, управляемый через инфраструктуру как код.