ТестированиеМобильное тестированиеИнженер по мобильному тестированию

После обновления приложения до версии со схемой базы данных 2 нужно убедиться, что данные пользователя не п...

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

function launch() {
    db = openDatabase('app.db', schemaVersion = 2)
    profile = db.read('profile')
    render(profile.displayName)
}
Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Нужно установить старую версию приложения, создать в ней реальные данные, обновить приложение поверх неё без удаления, затем проверить запуск, содержимое данных и версию схемы. Отдельно следует проверить чистую установку, но она не подтверждает миграцию: при ней база создаётся сразу в актуальном формате.

Исторический контекст

Мобильные приложения хранят пользовательские данные в локальной базе, файлах или защищённом хранилище. При выпуске новой версии структура этих данных может измениться, поэтому появился подход с версионированием схемы и последовательными миграциями.

Исходная проблема — обновление должно сохранить существующие данные, хотя новая версия ожидает другой формат. Простая инициализация новой базы решает задачу только для первого запуска и не покрывает сценарий реального обновления.

Постановка проблемы

На устройстве уже могут находиться профиль, черновики, кэш, настройки и записи офлайн-операций. Если приложение открыть после обновления без миграции или выполнить её частично, возможны потеря данных, ошибка чтения, повреждение записей или повторная авторизация пользователя.

Критичный сценарий — обновление поверх установленной версии. Установка версии 2 после удаления версии 1 проверяет только совместимость нового приложения с пустым хранилищем и может скрыть ошибку преобразования старых данных.

Подробное решение

Базовый тестовый сценарий выглядит так:

  1. Установить версию 1 приложения.
  2. Создать набор данных, характерный для старой схемы: заполненный профиль, необязательные поля, длинные строки, несколько записей и незавершённый черновик.
  3. Зафиксировать состояние локального хранилища и версию схемы.
  4. Установить версию 2 поверх версии 1, не очищая данные.
  5. Запустить приложение и проверить успешное открытие базы, результат миграции, отсутствие потери данных и корректное отображение новых полей.
  6. Перезапустить приложение и убедиться, что миграция не выполняется повторно и не создаёт дубликаты.

Минимальная модель миграции может выглядеть так:

function openDatabase(path, targetVersion) { db = storage.open(path) while db.version < targetVersion { next = db.version + 1 beginTransaction() migrate(db, next) db.version = next commitTransaction() } return db }

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

Нужно проверять не только наличие приложения после обновления, но и обратную совместимость данных с пользовательскими сценариями. Важны также обновления через несколько версий, например 1 → 3 и 1 → 2 → 3, если продукт действительно поддерживает оба пути.

На iOS и Android обычное обновление обычно сохраняет данные приложения, но поведение резервного восстановления, очистки данных, корпоративного управления устройством и удаления приложения нужно учитывать отдельно. Нельзя считать данные после переустановки эквивалентными данным после обновления: например, некоторые виды системного защищённого хранения могут иметь отличные от файловой базы правила жизненного цикла.

Компромисс состоит в выборе стратегии миграций. Последовательные миграции проще проверять и надёжнее для пропуска нескольких версий, но увеличивают код и время запуска. Пересоздание базы проще реализовать, однако оно допустимо только для данных, которые можно безопасно восстановить или потерять.

Ситуация из практики

После выпуска версии 3 часть пользователей стала видеть пустой список заказов. На чистой установке ошибка не воспроизводилась, а команда сначала предложила удалять и создавать базу заново.

Рассматривались варианты:

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

Выбрали третий вариант. Тестировщики подготовили базы версий 1 и 2 с граничными данными, обновляли их до версии 3, прерывали миграцию и повторяли запуск. После исправления миграция стала либо полностью применяться, либо откатываться, а данные заказов сохранялись.

Что кандидаты часто упускают

  1. Вопрос: Что изменится в проверке, если приложение должно поддерживать обновление сразу с версии 1 до версии 4?

    Ответ: Нужно проверить цепочку миграций 1 → 2 → 3 → 4 либо явно доказать наличие прямой миграции 1 → 4. Нельзя предполагать, что пользователь обязательно запускал каждую промежуточную версию: магазин приложений может обновить приложение через несколько релизов. Также стоит проверить промежуточное состояние после каждого шага, чтобы ошибка на ранней миграции не маскировалась последующей.

  2. Вопрос: Как проверить устойчивость миграции к принудительному завершению приложения?

    Ответ: Следует прерывать процесс на разных этапах транзакции, например закрытием приложения, перезагрузкой устройства или остановкой процесса, а затем снова запускать приложение. Ожидаемый результат — старая целостная схема либо полностью новая схема, но не смешанное состояние. Для этого миграция должна быть атомарной или иметь механизм возобновления с понятным маркером состояния.

  3. Вопрос: Почему проверка только структуры базы недостаточна?

    Ответ: Схема может формально соответствовать версии, но данные при преобразовании могут потерять значения, изменить кодировку, нарушить связи или получить неверные значения по умолчанию. Поэтому нужно проверять пользовательские инварианты: идентификаторы сохраняются, количество записей не уменьшается без предусмотренной причины, обязательные поля валидны, а приложение может редактировать и повторно сохранять мигрированные данные. Такая проверка выявляет ошибки преобразования, которые не видны при чтении одной лишь версии схемы.