Интеграционные тесты, работающие с реальным IndexedDB, проверяют
поведение кода в условиях, максимально приближенных к браузерной среде.
Библиотека localForage опирается на IndexedDB как на основной backend в
современных браузерах, поэтому корректная проверка взаимодействия с ним
требует отказа от упрощённых моков и использования полноценного
хранилища.
IndexedDB отличается асинхронной моделью, транзакционностью и
особенностями реализации в разных браузерах. Именно эти свойства делают
интеграционные тесты критически важными: они выявляют проблемы, которые
невозможно воспроизвести в unit-тестах с заглушками.
Ключевые особенности поведения IndexedDB, влияющие на
тестирование:
- асинхронные операции чтения и записи;
- строгая модель транзакций;
- задержки и непредсказуемый порядок выполнения микротасков;
- различия реализации в Chromium, Firefox и WebKit;
- ограничения среды выполнения (например, Node.js без браузерного
контекста).
localForage скрывает эти сложности, предоставляя единый Promise-based
API, однако тестовая среда всё равно должна учитывать реальную природу
хранилища.
Подготовка тестовой среды
с IndexedDB
Интеграционные тесты с IndexedDB выполняются в браузероподобной
среде. На практике используется несколько подходов:
Браузерные раннеры
Наиболее достоверный вариант — запуск тестов в реальном браузере
через инструменты:
- Playwright
- Puppeteer
- Cypress
Такая среда обеспечивает:
- полноценный IndexedDB;
- реальное поведение event loop;
- корректную работу localForage без эмуляции.
Node.js с jsdom
jsdom частично эмулирует браузер, но IndexedDB в нём отсутствует или
реализован ограниченно. Это делает его непригодным для полноценного
тестирования localForage без дополнительных полифилов.
Полифилы IndexedDB
Для Node.js-среды используются реализации:
- fake-indexeddb
- indexeddbshim
Они позволяют запускать тесты в CI, но важно учитывать:
- поведение может отличаться от реального браузера;
- возможны расхождения в транзакциях;
- нет полной идентичности производительности и блокировок.
Конфигурация localForage
для тестов
Перед запуском тестов важно обеспечить изолированную конфигурацию
хранилища.
Типичная настройка:
- отдельный
name базы;
- уникальный
storeName для каждого тестового набора;
- использование IndexedDB driver в приоритете.
Ключевой принцип — изоляция данных между тестами.
Основные параметры:
driver: localForage.INDEXEDDB;
name: тестовая база;
storeName: уникальный namespace;
- отключение fallback на WebSQL/LocalStorage при необходимости
детерминированности.
Очистка состояния между
тестами
Стабильность интеграционных тестов напрямую зависит от корректной
очистки IndexedDB.
Полное удаление базы
Самый надёжный способ — удаление базы целиком:
indexedDB.deleteDatabase(name)
Этот подход гарантирует отсутствие остаточных данных и блокировок
транзакций.
Очистка через localForage
API
Менее радикальный способ:
Он очищает store, но:
- не удаляет структуру базы;
- может сохранять метаданные в некоторых реализациях;
- быстрее, но менее изолирован.
Временные артефакты
транзакций
IndexedDB может удерживать соединения даже после очистки. Поэтому
часто требуется:
- ожидание завершения всех транзакций;
- принудительное закрытие соединений в тестовом окружении;
- последовательный запуск тестов при нестабильности среды.
Асинхронная природа
тестов localForage
Все операции localForage возвращают Promise, что требует строгого
контроля асинхронности.
Базовые операции
setItem
getItem
removeItem
clear
Каждая операция:
- выполняется в отдельной транзакции IndexedDB;
- может завершиться в следующем тике event loop;
- не гарантирует синхронность даже при последовательных вызовах.
Типичная ошибка тестирования
Ошибкой является предположение о моментальном сохранении:
localForage.setItem('key', 'value');
expect(localForage.getItem('key')).toBe('value');
Такой код приводит к гонкам, поскольку setItem не
завершён.
Корректная форма требует ожидания Promise:
await localForage.setItem('key', 'value');
const result = await localForage.getItem('key');
Гонки и нестабильность в
IndexedDB
IndexedDB допускает конкурентные операции, но их порядок не всегда
очевиден.
Проблемные сценарии
- параллельные записи одного ключа;
- одновременные
clear() и setItem();
- чтение во время миграции структуры;
- повторные инициализации localForage.
Типичный race condition
Если два теста используют одну базу:
- один тест вызывает
clear();
- второй выполняет
setItem() одновременно;
- итоговое состояние становится недетерминированным.
Решение — изоляция или последовательный запуск.
Стратегии изоляции тестов
Уникальные базы на тест
Каждый тест создаёт собственную базу:
- уникальное имя через UUID;
- очистка после завершения;
- минимизация конфликтов транзакций.
Изоляция через beforeEach /
afterEach
Типовой паттерн:
beforeEach: инициализация localForage;
afterEach: удаление базы.
Полное разделение контекстов
В Playwright или Puppeteer можно:
- запускать отдельную страницу на тест;
- изолировать IndexedDB storage context;
- предотвращать утечки состояния между тестами.
Миграции данных и
тестирование схем
localForage не предоставляет строгой схемы данных, но приложения
часто реализуют версионирование вручную.
Проблемы миграций
- изменение структуры объекта;
- добавление новых ключей;
- преобразование старых значений;
- несовместимость старых записей.
Тестирование миграций
Интеграционные тесты должны проверять:
- корректную загрузку старых данных;
- автоматическую трансформацию структуры;
- отсутствие потери информации;
- идемпотентность миграций.
Типичный сценарий:
- записать данные старой версии;
- изменить версию схемы;
- перезапустить инициализацию;
- проверить трансформацию.
Поведение IndexedDB в
CI-средах
CI окружения часто становятся источником нестабильности тестов.
Основные проблемы
- отсутствие полноценного IndexedDB;
- ограниченные файловые системы;
- параллельные процессы;
- замедление event loop.
Решения
- использование headless Chrome с Playwright;
- фиксация версии браузера;
- отключение параллельного запуска тестов;
- применение fake-indexeddb только для unit-уровня.
Производительность
интеграционных тестов
IndexedDB может быть медленнее in-memory хранилищ, особенно при:
- больших объёмах данных;
- частых транзакциях;
- последовательных операциях записи.
Оптимизации
- минимизация данных в тестах;
- группировка операций;
- избегание ненужных
clear() между шагами;
- повторное использование открытой базы в одном тесте.
Отладка поведения
localForage
При работе с IndexedDB важно уметь диагностировать состояние
базы.
Подходы к отладке
- DevTools Application → IndexedDB;
- логирование всех операций wrapper-слоем;
- перехват Promise-цепочек;
- инспекция транзакций через браузерные инструменты.
Частые проблемы
- “зависшие” транзакции;
- несоответствие между записью и чтением;
- потеря данных при очистке;
- неожиданные fallback-драйверы.
Контроль
fallback-драйверов localForage
localForage может переключаться между драйверами:
- IndexedDB;
- WebSQL (устаревший);
- localStorage.
В тестах важно фиксировать поведение:
- принудительное указание
INDEXEDDB;
- проверка выбранного драйвера через
getDriver() или
внутренние API;
- запрет fallback при тестировании критичных сценариев.
Поведение при
конкурентных инициализациях
Частая ошибка — многократный вызов config() и повторная
инициализация localForage.
Это может привести к:
- созданию нескольких connection pools;
- конфликтам транзакций;
- непредсказуемому поведению setItem/getItem.
Тесты должны проверять:
- идемпотентность инициализации;
- устойчивость к повторным вызовам;
- корректное переиспользование базы.
Стабилизация асинхронных
ожиданий
В IndexedDB нет синхронных гарантий завершения операций, поэтому
тесты требуют явного ожидания.
Типовые ошибки:
- отсутствие
await;
- использование таймеров вместо Promise;
- попытка синхронной проверки состояния.
Корректный подход:
- строго Promise-based ожидания;
- последовательные цепочки операций;
- контроль завершения транзакций перед проверками.
Надёжность интеграционных
тестов
Ключевая цель тестирования localForage с IndexedDB — не просто
проверка API, а выявление:
- гонок;
- утечек состояния;
- неконсистентности данных;
- ошибок транзакционного уровня.
Надёжные тесты всегда учитывают реальное поведение браузерного
хранилища, а не его упрощённые модели.