Этот вопрос касается различий между эмулированной средой тестирования и реальным браузером, особенно в части доступных API.
Основные отличия Jest с jsdom от реального браузера включают: отсутствие или ограниченная реализация многих Web APIs (clipboard, geolocation, media queries), нет реального рендеринга на экране, ограниченная поддержка CSS, отсутствие реальных сетевых запросов и навигации. Также отличаются производительность, возможности отладки и обработка ошибок.
Хотя jsdom предоставляет хорошую эмуляцию браузера, существуют значительные различия с реальной средой выполнения.
Ключевые отличия:
1. Отсутствующие или ограниченные Web APIs:
Clipboard API:
// В реальном браузере
await navigator.clipboard.writeText('text');
// В Jest/jsdom - не работает
// Требуется мокинг
Object.assign(navigator, {
clipboard: {
writeText: jest.fn(),
},
});Geolocation API:
// Мокинг для тестов
const mockGeolocation = {
getCurrentPosition: jest.fn(),
watchPosition: jest.fn(),
};
global.navigator.geolocation = mockGeolocation;2. Ограничения рендеринга:
CSS и стили:
Ограниченная поддержка CSSOM
Media queries не работают
Нет реальных вычислений стилей
Отсутствие визуальной проверки
Размеры и layout:
// В jsdom многие свойства возвращают 0
element.getBoundingClientRect(); // {x: 0, y: 0, width: 0, height: 0}
// Требуется мокинг
Element.prototype.getBoundingClientRect = jest.fn(() => ({
width: 100,
height: 50,
top: 0,
left: 0,
bottom: 50,
right: 100,
}));3. Сетевые возможности:
Реальные vs эмулированные запросы:
// Jest использует моки для fetch/XMLHttpRequest
global.fetch = jest.fn();
fetch.mockResolvedValue({
json: () => Promise.resolve({ data: 'test' })
});4. Производительность и поведение:
Таймеры и анимации:
requestAnimationFrame работает иначе
Таймеры могут быть "подделаны" с jest.useFakeTimers()
Отсутствие реального GPU ускорения
Пример обработки отличий:
// Адаптация тестов под ограничения jsdom
beforeEach(() => {
// Мокинг отсутствующих API
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
});