Работа с IndexedDB через Dexie.js почти всегда связана с состоянием, которое сохраняется между сессиями. В отличие от обычных in-memory структур, база данных живёт независимо от жизненного цикла теста, процесса или даже перезапуска окружения. Это создаёт ключевую проблему тестирования: состояние одного теста может повлиять на другой.
Изоляция тестов в Dexie.js опирается на два базовых принципа: полное управление жизненным циклом базы данных и гарантированное очищение всех ресурсов после выполнения теста.
IndexedDB не является временным хранилищем. Даже при повторном запуске тестов:
В контексте Dexie.js это особенно важно, так как Dexie активно кеширует соединения и управляет версионностью схемы.
Существует несколько устойчивых подходов к обеспечению чистого состояния между тестами:
Каждый тест или тестовый набор использует отдельное имя БД:
const db = new Dexie(`TestDB_${Date.now()}_${Math.random()}`);
Такой подход гарантирует отсутствие пересечений, но приводит к накоплению мусора, если база не удаляется.
Dexie предоставляет прямой механизм удаления:
await db.delete();
или статический вариант:
await Dexie.delete('TestDB');
Этот способ является наиболее надёжным при тестировании, так как полностью удаляет:
Важно учитывать, что удаление базы возможно только при отсутствии открытых соединений.
Если схема стабильна и переиспользуется, можно очищать только данные:
await db.table('users').clear();
await db.table('orders').clear();
или массово через транзакцию:
await db.transaction('rw', db.tables, async () => {
await Promise.all(db.tables.map(t => t.clear()));
});
Этот подход быстрее удаления базы, но не решает проблему миграций и конфликтов состояния.
Dexie активно держит соединение с IndexedDB открытым. Это может мешать:
Правильный порядок завершения работы:
db.close();
await db.delete();
Если соединение не закрыто, операция удаления может быть проигнорирована или завершиться неуспешно.
В Node.js и некоторых тестовых средах используется эмуляция IndexedDB через fake-indexeddb.
Подключение обычно выглядит так:
import 'fake-indexeddb/auto';
import Dexie from 'dexie';
Преимущества:
Ограничения:
В средах вроде Jest ключевая задача — гарантировать чистый контекст
между test или it.
Типовой подход:
let db;
beforeEach(async () => {
db = new Dexie('TestDB');
db.version(1).stores({
users: '++id,name'
});
await db.open();
});
afterEach(async () => {
db.close();
await db.delete();
});
Критически важно:
Одной из наиболее частых причин нестабильных тестов является ситуация, когда:
Это проявляется в виде:
VersionError;Решение:
await для всех Dexie-операций;Миграции в Dexie.js добавляют дополнительный слой сложности. Если тесты используют разные версии схемы, необходимо:
Пример безопасного паттерна:
await Dexie.delete('TestDB');
const db = new Dexie('TestDB');
db.version(2).stores({
users: '++id,name,age'
});
await db.open();
Dexie поддерживает атомарные транзакции, которые могут использоваться для локальной изоляции логики внутри одного теста:
await db.transaction('rw', db.users, async () => {
await db.users.add({ name: 'A' });
await db.users.add({ name: 'B' });
});
Однако транзакции не решают проблему глобального состояния между тестами — они изолируют только операции внутри одного выполнения.
import Dexie from 'dexie';
import 'fake-indexeddb/auto';
export function createTestDB(name = 'TestDB') {
const db = new Dexie(name);
db.version(1).stores({
users: '++id,name'
});
return db;
}
export async function setupDB() {
const db = createTestDB();
await db.open();
return db;
}
export async function teardownDB(db) {
db.close();
await Dexie.delete(db.name);
}
Такой подход позволяет:
При параллельном выполнении тестов особенно важно:
delete, а не только
clear;