Проверка миграций Liquibase с помощью Database Rider

Проверка миграций Liquibase с помощью Database Rider

Как часто вы сталкиваетесь с неприятным кейсом - запускаем миграцию liquibase на стенде, а она падает с ошибкой или данные становятся неконсистентны? При этом на машине разработчика все проходит успешно...

Как снизить риски ошибок при миграции схемы данных?

Отдельно тестировать миграции данных, в этом нам поможет - Database Rider. Database Rider в сочетании с Liquibase позволяет создать надежную систему автоматизированной проверки миграций.

Сегодня разберем, как можно достаточно легко тестировать миграции схем данных.

Почему важно тестировать миграции?

Миграции баз данных — это критически важная часть приложения. Ошибка в миграции может привести к:

  • Потере данных в проде.
  • Несовместимости между версиями приложения
  • Дорогостоящему восстановлению

Необходимые настройки

Воспользуемся предыдущим примером, где мы поднимали Test Containers и изменим его для наших нужд.

1 Подключаем liquibase:

Добавим зависимости liquibase в build.gradle:

dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // Добавляем зависимость для Liquibase implementation 'org.liquibase:liquibase-core' // ... }

Также необходимо добавить настройки в application.properties чтобы миграции начали работать:

# Liquibase configuration spring.liquibase.enabled=true spring.liquibase.change-log=classpath:/db/changelog/db.changelog-master.yaml

Также изменим конфигурацию для тестов:

spring: datasource: url: jdbc:tc:postgresql:16-alpine:///testdb driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver liquibase: enabled: false # Важно отключить валидацию схемы jpa.hibernate.ddl-auto: none

При тестировании миграций есть вероятность, что наши JPA сущности в какой-то момент будут находится в неконсистентном состоянии со схемой данных, поэтому важно отключить валидацию схемы в Hibernate: jpa.hibernate.ddl-auto: none.

2 Добавляем миграции

Теперь добавим лог миграции базы данных db.changelog-master.yaml:

databaseChangeLog: - include: file: changes/001-create-users-info-table.yaml relativeToChangelogFile: true - changeSet: id: tag-version-1 author: kit changes: - tagDatabase: tag: v1 - include: file: changes/002-users-info-add-status.yaml relativeToChangelogFile: true

В 002-users-info-add-status.yaml мы к таблице users_info добавим колонку status. Соответствующие изменения применим и к JPA сущности User.

Обратите внимание на чейнджсет с id: tag-version-1, здесь мы добавляем тэг к БД. Это позволит нам гибко управлять миграциями и накатывать изменения по версиям тэгов.

3 Пишем тест для проверки миграции

Настало время реализации теста миграции. Сначала подготовим наборы данных, необходимых для проверки миграции:

  • datasets/migrations/v1/users.yml - начальное состояние данных;
  • datasets/migrations/v1/expected-users.yml - конечное состояние, после проведенных миграций.

Суть теста довольно проста:

  • Применяем миграцию до версии v1 перед тестом.
  • Применяем миграцию до следующей версии.
  • Используя механизмы Database Rider проверяем конечное состояние базы данных.

Полный код теста:

@SpringBootTest @DBRider @ActiveProfiles("test") @Testcontainers @DBUnit(schema = "public", caseSensitiveTableNames = true) @TestPropertySource(properties = { "spring.liquibase.enabled=false" // отключаем автоприменение }) @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class UsersInfoAddStatusTest { @Autowired private DataSource dataSource; private Liquibase liquibase; @BeforeAll void runV1Migration() throws Exception { // применяем миграцию v1 liquibase = new Liquibase( "db/changelog/db.changelog-master.yaml", new ClassLoaderResourceAccessor(), DatabaseFactory.getInstance() .findCorrectDatabaseImplementation(new JdbcConnection(dataSource.getConnection())) ); liquibase.update("v1"); } @Test @DataSet(value = "datasets/migrations/v1/users.yml") // начальное состояние @ExpectedDataSet(value = "datasets/migrations/v1/expected-users.yml", ignoreCols = "id") // конечное состояние void testMigrationAfterV1() throws Exception { // Применяем остальные изменения (после v1) liquibase.update(); } }

Вывод

Database Rider предоставляет нам отличный гибкий функционал для проверки состояния БД и работы с тестовыми наборами данных.

Использование Database Rider совместно с Liquibase и Testcontainers позволит тестировать миграции схемы данных и данных. Это позволит снизить количество ошибок, возникающих в самый неподходящий момент.

Исходный код доступен по ссылке

Подпишись на мой telegram-канал

Начать дискуссию