Мокирование зависимостей — это техника тестирования, при которой реальные зависимости заменяются управляемыми объектами-заглушками (mock objects). Эти объекты имитируют поведение настоящих компонентов, но позволяют полностью контролировать их ответы, состояние и побочные эффекты.
В контексте интерфейсных библиотек, таких как Muuri, мокирование применяется при тестировании:
Muuri активно взаимодействует с:
Во время модульного тестирования реальные зависимости могут:
Мокирование решает эти проблемы.
Muuri — библиотека динамических сеток, работающая поверх DOM и использующая внутреннюю систему управления элементами.
К ключевым зависимостям относятся:
1. DOM API
Используются:
document.createElementgetBoundingClientRectappendChildremoveChild2. Система событий
Muuri генерирует события:
layoutStartlayoutEndmoveaddremove3. Drag & Drop механизм
Перетаскивание зависит от:
4. Анимационная система
Muuri использует:
5. Пользовательские функции
Например:
dragStartPredicatelayoutsortDataВсе эти зависимости могут быть замокированы.
При тестировании логики работы сетки необходимо изолировать:
Мокирование позволяет отключить:
DOM-операции и анимации значительно замедляют тесты.
Мок-объекты позволяют:
С помощью моков можно воспроизводить сложные ситуации:
В тестировании применяются несколько типов заменителей.
Возвращает заранее определённые данные.
Пример:
const getBoundingClientRectStub = () => ({
width: 200,
height: 100,
top: 0,
left: 0
});
Фиксирует вызовы и позволяет проверять их.
const mockCallback = jest.fn();
Наблюдает за вызовами реального метода.
jest.spyOn(grid, 'layout');
Полноценная упрощённая реализация.
Например, поддельный источник данных.
Muuri активно использует DOM-измерения.
Наиболее критичный метод:
element.getBoundingClientRect()
В тестах его часто заменяют.
Element.prototype.getBoundingClientRect = jest.fn(() => ({
width: 100,
height: 100,
top: 0,
left: 0,
right: 100,
bottom: 100
}));
Теперь Muuri всегда будет считать элементы одинакового размера.
Это делает тесты:
Muuri использует requestAnimationFrame для анимации
layout.
В тестовой среде его часто заменяют синхронной функцией.
global.requestAnimationFrame = (cb) => cb();
Это позволяет:
Drag-механизм Muuri сложен и зависит от событий браузера.
Во многих тестах перетаскивание отключают.
const grid = new Muuri(container, {
dragEnabled: false
});
Если требуется протестировать логику drag-событий, можно замокировать предикат запуска.
const grid = new Muuri(container, {
dragStartPredicate: () => true
});
Для более точного контроля используют mock-функции.
const dragPredicateMock = jest.fn(() => true);
Muuri позволяет переопределять алгоритм layout.
В тестах иногда требуется проверить:
Пример:
const layoutMock = jest.fn((grid, layoutId, items, width, height, callback) => {
callback({
id: layoutId,
items: items
});
});
Использование:
const grid = new Muuri(container, {
layout: layoutMock
});
Проверка:
expect(layoutMock).toHaveBeenCalled();
Muuri часто используется вместе с API или локальными хранилищами.
Например, сетка наполняется элементами после загрузки данных.
Реальный запрос:
fetch('/api/items')
В тестах такой запрос заменяют.
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([
{ id: 1 },
{ id: 2 }
])
})
);
Теперь тесты не зависят от сети.
Muuri генерирует события через собственную систему.
Пример:
grid.on('layoutEnd', handler);
Для тестирования можно использовать mock-обработчик.
const handler = jest.fn();
grid.on('layoutEnd', handler);
grid.layout();
expect(handler).toHaveBeenCalled();
Это позволяет проверять:
Иногда необходимо заменить внутренние методы.
Например:
grid.layout()
С помощью spy можно отслеживать вызовы.
const spy = jest.spyOn(grid, 'layout');
grid.add(element);
expect(spy).toHaveBeenCalled();
Это проверяет, что добавление элемента инициирует перерасчёт сетки.
Muuri поддерживает пользовательскую сортировку через
sortData.
Пример настройки:
const grid = new Muuri(container, {
sortData: {
price: (item, element) => {
return parseFloat(element.dataset.price);
}
}
});
В тестах можно замокировать вычисление.
const priceMock = jest.fn(() => 10);
Использование:
const grid = new Muuri(container, {
sortData: {
price: priceMock
}
});
Проверка:
expect(priceMock).toHaveBeenCalled();
Muuri использует:
setTimeoutrequestAnimationFrameДля тестов применяют fake timers.
Пример с Jest:
jest.useFakeTimers();
Теперь можно контролировать время.
jest.advanceTimersByTime(100);
Это позволяет тестировать:
Muuri вычисляет размеры контейнера при layout.
В тестах контейнер может не иметь реальных размеров.
Используется мок:
Object.defineProperty(container, 'clientWidth', {
value: 800
});
И:
Object.defineProperty(container, 'clientHeight', {
value: 600
});
Это гарантирует корректные вычисления сетки.
Метод:
grid.add(element)
может инициировать множество внутренних процессов.
В тестах иногда создают фиктивные элементы.
function createMockItem() {
const el = document.createElement('div');
el.className = 'item';
return el;
}
Использование:
const item = createMockItem();
grid.add(item);
Muuri изменяет:
Для тестов можно замокировать методы изменения стилей.
HTMLElement.prototype.style = {};
Или отслеживать изменения через spy.
const styleSpy = jest.spyOn(element.style, 'transform', 'set');
Удаление инициирует события:
grid.remove(item);
Проверка через mock:
const removeHandler = jest.fn();
grid.on('remove', removeHandler);
grid.remove(item);
expect(removeHandler).toHaveBeenCalled();
Muuri поддерживает фильтрацию:
grid.filter('.active');
Можно замокировать функцию фильтра.
const filterMock = jest.fn(() => true);
grid.filter(filterMock);
Проверка:
expect(filterMock).toHaveBeenCalled();
При мокировании важно сбрасывать состояние между тестами.
Пример:
beforeEach(() => {
jest.clearAllMocks();
});
Или:
afterEach(() => {
jest.restoreAllMocks();
});
Это предотвращает влияние предыдущих тестов.
Muuri может использоваться вместе с:
В таких случаях мокируются:
Пример для React-теста:
jest.mock('muuri');
Можно вернуть поддельную реализацию:
Muuri.mockImplementation(() => ({
add: jest.fn(),
remove: jest.fn(),
layout: jest.fn()
}));
Минимизация моков
Чрезмерное количество моков делает тесты хрупкими.
Мокируются только внешние зависимости.
Тестирование поведения, а не реализации
Проверяется:
А не внутренние детали реализации.
Детерминированность
Все источники случайности должны быть замокированы:
Очистка среды
Каждый тест должен запускаться в чистой среде:
Тест добавления элемента.
jest.useFakeTimers();
Element.prototype.getBoundingClientRect = jest.fn(() => ({
width: 100,
height: 100,
top: 0,
left: 0,
right: 100,
bottom: 100
}));
test('item addition triggers layout', () => {
const container = document.createElement('div');
const grid = new Muuri(container);
const layoutSpy = jest.spyOn(grid, 'layout');
const item = document.createElement('div');
grid.add(item);
expect(layoutSpy).toHaveBeenCalled();
});
Здесь замокированы:
Тест выполняется быстро и полностью контролируемо.