← Назад к вопросам

Что делать если нет возможности локально устанавливать базу данных для использования ее с Unit-тестами

3.0 Senior🔥 161 комментариев
#Базы данных и SQL#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Тестирование без локальной базы данных

Если у вас нет возможности установить реальную базу данных локально, есть несколько отличных решений для Unit и Integration тестирования. Вот полный спектр подходов от простого к сложному.

1. Встроенные (Embedded) базы данных

Это памятные БД, которые запускаются прямо в рамках теста, без необходимости отдельной установки.

H2 Database (самый популярный)

<!-- pom.xml -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
# application-test.yml
spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driverClassName: org.h2.Driver
    username: sa
    password: 
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: create-drop  # Создаёт и удаляет схему для каждого теста
@SpringBootTest
@ActiveProfiles("test")
class UserRepositoryTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @BeforeEach
    void setUp() {
        // База данных создаётся для каждого теста
    }
    
    @Test
    void testSaveUser() {
        User user = new User("John", "john@example.com");
        User saved = userRepository.save(user);
        
        assertNotNull(saved.getId());
        assertEquals("John", saved.getName());
    }
    
    @AfterEach
    void tearDown() {
        // База данных автоматически очищается
    }
}

Преимущества:

  • ✅ Работает в памяти (очень быстро)
  • ✅ Не нужна установка
  • ✅ Автоматически очищается
  • ✅ Полностью совместима с SQL

Недостатки:

  • ❌ Отличается от production БД (синтаксис, функции)
  • ❌ Может скрывать проблемы специфичные для PostgreSQL/MySQL

Derby (встроенная Apache база)

<dependency>
    <groupId>org.apache.derby</groupId>
    <artifactId>derby</artifactId>
    <scope>test</scope>
</dependency>
spring:
  datasource:
    url: jdbc:derby:memory:testdb;create=true
    driverClassName: org.apache.derby.jdbc.EmbeddedDriver
  jpa:
    database-platform: org.hibernate.dialect.DerbyDialect

2. TestContainers - лучший вариант для интеграционных тестов

Это Docker контейнеры, запускаемые во время тестирования. Дают вам полностью идентичную production среду.

Установка

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

<!-- PostgreSQL контейнер -->
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

<!-- MySQL контейнер -->
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>mysql</artifactId>
    <version>1.19.0</version>
    <scope>test</scope>
</dependency>

Пример с PostgreSQL

@SpringBootTest
class UserRepositoryIT {  // IT = Integration Test
    
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
        .withDatabaseName("testdb")
        .withUsername("test")
        .withPassword("password");
    
    @Autowired
    private UserRepository userRepository;
    
    @DynamicPropertySource
    static void configureTestDatabase(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }
    
    @Test
    void testUserOperations() {
        User user = new User("Alice", "alice@example.com");
        User saved = userRepository.save(user);
        
        User found = userRepository.findById(saved.getId()).orElse(null);
        assertNotNull(found);
        assertEquals("Alice", found.getName());
    }
}

Spring Boot 3.1+ (более удобно)

@SpringBootTest
class UserRepositoryIT {
    
    static {
        // Автоматически запускает контейнер
        Testcontainers.checks();
    }
    
    @ServiceConnection
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void testSaveUser() {
        User user = new User("Bob", "bob@example.com");
        User saved = userRepository.save(user);
        assertNotNull(saved.getId());
    }
}

Преимущества TestContainers:

  • ✅ Полностью идентична production БД
  • ✅ Работает с Docker (распространено везде)
  • ✅ Поддерживает все популярные БД (PostgreSQL, MySQL, MongoDB и т.д.)
  • ✅ Автоматическая очистка после теста
  • ✅ Легко с параметрами (версия БД, переменные среды)

Недостатки:

  • ❌ Нужен Docker установлен
  • ❌ Медленнее встроенных БД (нужно создать контейнер)
  • ❌ Для быстрых unit-тестов может быть оверкилл

3. Мокирование БД слоя (Repository Mock)

Вместо реальной БД мокируем Repository.

@Service
public class UserService {
    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public User getUserById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
    }
    
    public User createUser(String name, String email) {
        User user = new User(name, email);
        return userRepository.save(user);
    }
}

// Тест с мокированием
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private UserService userService;
    
    @Test
    void testGetUserById() {
        // Arrange
        Long userId = 1L;
        User mockUser = new User(userId, "John", "john@example.com");
        
        when(userRepository.findById(userId))
            .thenReturn(Optional.of(mockUser));
        
        // Act
        User result = userService.getUserById(userId);
        
        // Assert
        assertEquals("John", result.getName());
        verify(userRepository).findById(userId);
    }
    
    @Test
    void testGetUserByIdNotFound() {
        // Arrange
        Long userId = 999L;
        when(userRepository.findById(userId))
            .thenReturn(Optional.empty());
        
        // Act & Assert
        assertThrows(UserNotFoundException.class, 
            () -> userService.getUserById(userId)
        );
    }
}

Когда использовать:

  • ✅ Unit-тесты business логики
  • ✅ Изолированное тестирование сервисов
  • ✅ Быстрые тесты

Когда НЕ использовать:

  • ❌ Интеграционные тесты
  • ❌ Тестирование запросов в БД
  • ❌ Тестирование транзакций

4. In-Memory стратегия - комбинированный подход

Используем встроенную БД для быстрых интеграционных тестов:

@SpringBootTest
@ActiveProfiles("test")  // Использует application-test.yml
class UserRepositoryTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @BeforeEach
    void setUp() {
        userRepository.deleteAll();  // Очищаем перед каждым тестом
    }
    
    @Test
    void testFindByEmail() {
        // Arrange
        User user = new User("john@example.com", "John");
        userRepository.save(user);
        
        // Act
        User found = userRepository.findByEmail("john@example.com");
        
        // Assert
        assertNotNull(found);
        assertEquals("John", found.getName());
    }
}

5. Docker Compose для локальной разработки

Если у вас есть Docker, но не хочется использовать TestContainers:

# docker-compose.yml
version: 3.8
services:
  postgres:
    image: postgres:15
    environment:
      POSTGRES_USER: testuser
      POSTGRES_PASSWORD: password
      POSTGRES_DB: testdb
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
# Запускаем
docker-compose up -d

# Запускаем тесты
mvn test

# Останавливаем
docker-compose down

6. Рекомендуемая стратегия для разных сценариев

Сценарий 1: Запуск тестов в CI/CD (GitHub Actions, GitLab CI, Jenkins)

// Используем H2 или TestContainers (в CI уже есть Docker)
@SpringBootTest
@ActiveProfiles("test")  // Профиль для H2
class IntegrationTest {
    // Тесты
}

Сценарий 2: Локальная разработка

# application-dev.yml - реальная БД (Docker)
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: dev
    password: dev

# application-test.yml - встроенная БД
spring:
  datasource:
    url: jdbc:h2:mem:testdb

Сценарий 3: Быстрые unit-тесты

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock
    private UserRepository userRepository;  // Мокируем, не нужна БД
    
    @InjectMocks
    private UserService userService;
    
    // Быстрые тесты
}

Сценарий 4: Полные интеграционные тесты

@SpringBootTest
class FullIntegrationTest {
    
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>();
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private UserService userService;
    
    // Тесты с реальной логикой БД
}

7. Структура проекта

src/
├── main/
│   ├── java/com/example/
│   │   ├── domain/        (бизнес логика, не зависит от БД)
│   │   ├── service/       (сервисы)
│   │   └── repository/    (доступ в БД)
│   └── resources/
│       ├── application.yml           (production)
│       ├── application-dev.yml       (разработка)
│       └── application-test.yml      (тесты)
└── test/
    └── java/com/example/
        ├── service/       (unit-тесты с мокированием)
        ├── repository/    (интеграционные тесты с БД)
        └── integration/   (end-to-end тесты)

Итоговые рекомендации

СитуацияРешение
Быстрые unit-тестыМокирование (Mockito)
Интеграционные тесты, нет DockerH2 встроенная БД
Интеграционные тесты, есть DockerTestContainers
Локальная разработка без DockerDocker Compose
CI/CD pipelineTestContainers или H2
Production-like тестыTestContainers с реальной БД

Самый простой старт:

<!-- Добавьте в pom.xml -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>
# application-test.yml
spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driverClassName: org.h2.Driver
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    hibernate:
      ddl-auto: create-drop

Теперь вы можете писать интеграционные тесты без установки реальной БД!