idb-keyval — это простая библиотека для работы с
IndexedDB в браузере через промисы. Она предоставляет функции
get, set, del,
clear, keys и update для удобного
хранения и извлечения данных. При тестировании приложений, которые
используют idb-keyval, часто возникает необходимость
изолировать логику приложения от реальной IndexedDB,
чтобы юнит-тесты выполнялись быстро, предсказуемо и без побочных
эффектов. Для этого применяют мокирование.
idb-keyvalЕсли используется Jest, функции idb-keyval можно
заменить на заглушки через jest.mock:
import * as idbKeyval from 'idb-keyval';
jest.mock('idb-keyval', () => ({
get: jest.fn(),
set: jest.fn(),
del: jest.fn(),
clear: jest.fn(),
keys: jest.fn(),
update: jest.fn(),
}));
После этого можно задавать конкретное поведение моков:
idbKeyval.get.mockResolvedValue({ username: 'testUser' });
idbKeyval.set.mockResolvedValue(undefined);
mockResolvedValue(value) используется для имитации
успешного промиса.mockRejectedValue(error) имитирует отказ промиса.Для сложных проектов полезно создавать собственную обёртку над
idb-keyval. Например:
export const storage = {
getItem: (key) => idbKeyval.get(key),
setItem: (key, value) => idbKeyval.set(key, value),
removeItem: (key) => idbKeyval.del(key),
clear: () => idbKeyval.clear(),
};
В тестах достаточно мокировать только storage:
jest.mock('./storage', () => ({
storage: {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
},
}));
Это позволяет не трогать реальный
idb-keyval и централизованно управлять поведением
мока.
import { storage } from './storage';
test('сохраняет объект пользователя', async () => {
const user = { id: 1, name: 'Alice' };
storage.setItem.mockResolvedValue(undefined);
await storage.setItem('user', user);
expect(storage.setItem).toHaveBeenCalledWith('user', user);
});
test('получает объект пользователя', async () => {
const user = { id: 1, name: 'Alice' };
storage.getItem.mockResolvedValue(user);
const result = await storage.getItem('user');
expect(result).toEqual(user);
expect(storage.getItem).toHaveBeenCalledWith('user');
});
test('обработка ошибки при чтении', async () => {
const error = new Error('DB error');
storage.getItem.mockRejectedValue(error);
await expect(storage.getItem('user')).rejects.toThrow('DB error');
});
let mockDB = {};
const mockStorage = {
getItem: jest.fn((key) => Promise.resolve(mockDB[key])),
setItem: jest.fn((key, value) => {
mockDB[key] = value;
return Promise.resolve();
}),
removeItem: jest.fn((key) => {
delete mockDB[key];
return Promise.resolve();
}),
clear: jest.fn(() => {
mockDB = {};
return Promise.resolve();
}),
};
beforeEach для очистки объекта:beforeEach(() => {
mockDB = {};
});
updateupdate немного отличается: принимает ключ и функцию
обновления значения:
idbKeyval.update('counter', oldValue => (oldValue || 0) + 1);
Мок можно сделать так:
mockStorage.update = jest.fn((key, updater) => {
const oldValue = mockDB[key];
const newValue = updater(oldValue);
mockDB[key] = newValue;
return Promise.resolve(newValue);
});
Это позволяет имитировать цепочки обновлений точно так же, как в реальной IndexedDB.
idb-keyval, которую затем легко мокировать.toHaveBeenCalledWith,
чтобы убедиться, что логика приложения формирует правильные ключи и
значения.mockDB) для повторяемости тестов и возможности тестировать
сценарии с несколькими изменениями данных.Эти подходы обеспечивают полный контроль над поведением
idb-keyval в тестах, сохраняют чистоту и скорость
юнит-тестов и позволяют проверять ошибки, изменения и правильность
вызовов методов.