Юнит-тесты компонентов

Haunted — это небольшая библиотека для создания веб-компонентов с использованием функционального подхода и хуков, вдохновлённых React. При разработке компонентов важно обеспечить их корректное поведение через юнит-тесты, которые позволяют проверять отдельные функции и рендеринг компонентов без запуска всего приложения.


Организация тестов

Юнит-тесты для Haunted-компонентов обычно пишутся с использованием стандартных тестовых фреймворков, таких как Jest или Vitest, а для проверки DOM — @testing-library/dom или @testing-library/web-component. Основная цель — проверка состояния и вывода компонента на основе различных входных данных.

Файл с тестами рекомендуется организовывать рядом с компонентом:

src/
  components/
    my-counter.js
    my-counter.test.js

Настройка окружения

Для тестирования Haunted-компонентов необходимо, чтобы среда поддерживала Custom Elements и Shadow DOM. В Node.js это обеспечивается через библиотеки типа jsdom, которая имитирует браузерное окружение.

Пример базовой конфигурации Jest:

module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
};

Тестирование рендеринга

Компоненты Haunted определяются через функцию, которая возвращает шаблон html. Для проверки рендеринга важно убедиться, что элемент корректно создаётся и отображает данные.

import { html, render } from 'haunted';
import { screen } from '@testing-library/dom';
import './my-counter.js';

test('рендерит начальное значение', () => {
  const element = document.createElement('my-counter');
  document.body.appendChild(element);
  expect(element.shadowRoot.textContent).toContain('0');
});

Ключевой момент: все тесты должны работать с shadowRoot, так как Haunted по умолчанию использует Shadow DOM.


Тестирование состояния и хуков

Haunted поддерживает хуки вроде useState и useEffect. Для их тестирования важно учитывать, что обновление состояния вызывает повторный рендер. Использование await Promise.resolve() позволяет дождаться следующего цикла обновления DOM.

import { html, useState } from 'haunted';

function Counter() {
  const [count, setCount] = useState(0);
  return html`
    <button @click=${() => setCount(count + 1)}>Increment</button>
    <span>${count}</span>
  `;
}

customElements.define('my-counter', Counter);

test('увеличивает значение при клике', async () => {
  const element = document.createElement('my-counter');
  document.body.appendChild(element);

  const button = element.shadowRoot.querySelector('button');
  button.click();

  await Promise.resolve(); // ожидание обновления

  const span = element.shadowRoot.querySelector('span');
  expect(span.textContent).toBe('1');
});

Важно: Haunted обновляет DOM асинхронно после изменения состояния, поэтому синхронные проверки часто будут некорректны.


Мокинг внешних зависимостей

Если компонент взаимодействует с API или внешними функциями, их стоит мокировать для изоляции юнит-тестов:

import * as api from '../api.js';

jest.mock('../api.js', () => ({
  fetchData: jest.fn(() => Promise.resolve({ value: 42 }))
}));

test('отображает данные из API', async () => {
  const element = document.createElement('data-display');
  document.body.appendChild(element);

  await Promise.resolve(); // ожидание useEffect

  expect(element.shadowRoot.textContent).toContain('42');
});

Снапшот-тестирование

Haunted-компоненты хорошо подходят для снапшот-тестов, особенно когда нужно проверять стабильность HTML-разметки после изменений кода:

import { html, render } from 'haunted';
import './my-counter.js';

test('снапшот начального рендера', () => {
  const element = document.createElement('my-counter');
  document.body.appendChild(element);
  expect(element.shadowRoot.innerHTML).toMatchSnapshot();
});

Совет: снапшоты в сочетании с проверкой состояния помогают обнаружить неожиданные изменения в компоненте.


Проверка пользовательских событий

Haunted позволяет использовать стандартные DOM-события. В юнит-тестах можно симулировать клики, фокус или другие события:

test('вызывает событие customEvent', () => {
  const element = document.createElement('my-button');
  const handler = jest.fn();
  element.addEventListener('clicked', handler);

  element.shadowRoot.querySelector('button').click();

  expect(handler).toHaveBeenCalled();
});

Это особенно важно для компонентов, которые взаимодействуют с внешними обработчиками.


Рекомендации по организации тестов

  • Тестировать каждый хук и его побочные эффекты отдельно.
  • Использовать shadowRoot для всех проверок DOM.
  • Разделять тесты на рендеринг, состояние, события и интеграцию с внешними данными.
  • Асинхронные обновления всегда проверять через await Promise.resolve() или waitFor из @testing-library.

Юнит-тесты компонентов Haunted позволяют выявлять ошибки на раннем этапе, обеспечивают стабильность кода и делают рефакторинг безопасным. Их структурированный подход с фокусом на состояние, рендер и события обеспечивает качественное покрытие функционала без запуска полного браузера.