Интеграционные тесты

Интеграционные тесты в контексте SkateJS позволяют проверять работу компонентов в связке с другими элементами приложения, обеспечивая корректное взаимодействие DOM, свойств и событий. SkateJS — это библиотека для создания веб-компонентов на базе Custom Elements и Virtual DOM, что делает интеграционные тесты особенно важными для проверки жизненного цикла компонентов и их реактивности.


Настройка окружения для интеграционных тестов

Для тестирования SkateJS-компонентов рекомендуется использовать комбинацию следующих инструментов:

  • Jest — фреймворк для запуска тестов, работы с моками и снимками DOM.
  • Testing Library (DOM Testing Library) — для работы с DOM и проверки состояния элементов.
  • jsdom — эмулирует браузерное окружение, необходимое для рендеринга веб-компонентов вне браузера.

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

{
  "testEnvironment": "jsdom",
  "transform": {
    "^.+\\.jsx?$": "babel-jest"
  },
  "setupFilesAfterEnv": ["@testing-library/jest-dom/extend-expect"]
}

Эта конфигурация обеспечивает работу виртуального DOM, поддержку современных возможностей JavaScript и расширений для удобной проверки состояния элементов.


Рендеринг компонента для теста

SkateJS предоставляет функцию render и API компонента для интеграционных тестов. Важно проверять не только рендеринг, но и реактивное обновление свойств и событий.

Пример интеграционного теста для простого компонента <my-counter>:

import { define, props, h } from 'skatejs';
import { render } from '@testing-library/dom';

class MyCounter extends HTMLElement {
  static props = {
    count: props.number
  };

  connectedCallback() {
    this.render();
  }

  render() {
    this.innerHTML = `<span data-testid="count">${this.count || 0}</span>`;
  }
}

define('my-counter', MyCounter);

test('Counter отображает начальное значение и обновляется', () => {
  const container = document.createElement('div');
  document.body.appendChild(container);

  const counter = document.createElement('my-counter');
  counter.count = 5;
  container.appendChild(counter);

  const span = container.querySelector('[data-testid="count"]');
  expect(span.textContent).toBe('5');

  counter.count = 10;
  counter.render();
  expect(span.textContent).toBe('10');
});

Ключевые моменты:

  • Использование data-testid упрощает поиск элементов в DOM.
  • Свойства компонента изменяются напрямую через API SkateJS (counter.count).
  • Для обновления DOM вызывается метод render(), что имитирует реактивное обновление.

Тестирование взаимодействий и событий

Компоненты часто должны реагировать на пользовательские события или события других компонентов. В SkateJS можно использовать стандартный механизм событий DOM.

Пример проверки события increment в <my-counter>:

test('Counter генерирует событие при увеличении', () => {
  const counter = document.createElement('my-counter');
  document.body.appendChild(counter);

  const mockHandler = jest.fn();
  counter.addEventListener('increment', mockHandler);

  // Имитация пользовательского действия
  counter.dispatchEvent(new CustomEvent('increment', { detail: 1 }));

  expect(mockHandler).toHaveBeenCalledTimes(1);
  expect(mockHandler.mock.calls[0][0].detail).toBe(1);
});

Особенности:

  • События могут быть кастомными (CustomEvent) или стандартными (click, input).
  • Проверяется не только факт вызова обработчика, но и переданные данные (detail).

Интеграция с другими компонентами

SkateJS поддерживает вложенные компоненты. Интеграционные тесты помогают убедиться, что взаимодействие между ними корректно.

Пример теста родительского компонента, содержащего <my-counter>:

class CounterWrapper extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `<my-counter count="2"></my-counter>`;
  }
}

define('counter-wrapper', CounterWrapper);

test('Wrapper корректно рендерит вложенный Counter', () => {
  const wrapper = document.createElement('counter-wrapper');
  document.body.appendChild(wrapper);

  const counter = wrapper.querySelector('my-counter');
  expect(counter).not.toBeNull();
  expect(counter.count).toBe(2);
});

Особенности:

  • Вложенные компоненты должны корректно инициализироваться при подключении к DOM.
  • Интеграционные тесты проверяют передачу свойств и взаимодействие событий.

Асинхронные операции

SkateJS позволяет использовать асинхронные методы жизненного цикла, например, для загрузки данных.

Пример интеграционного теста асинхронного рендера:

class AsyncCounter extends HTMLElement {
  static props = { count: props.number };

  async connectedCallback() {
    const data = await this.fetchData();
    this.count = data.count;
    this.render();
  }

  async fetchData() {
    return new Promise(resolve => setTimeout(() => resolve({ count: 7 }), 50));
  }

  render() {
    this.innerHTML = `<span data-testid="count">${this.count}</span>`;
  }
}

define('async-counter', AsyncCounter);

test('AsyncCounter корректно рендерится после асинхронной загрузки', async () => {
  const counter = document.createElement('async-counter');
  document.body.appendChild(counter);

  await new Promise(r => setTimeout(r, 60));

  const span = counter.querySelector('[data-testid="count"]');
  expect(span.textContent).toBe('7');
});

Особенности:

  • Используется await для ожидания завершения асинхронных операций.
  • Проверяется финальное состояние компонента после загрузки данных.

Стратегии организации интеграционных тестов

  1. Изолированное окружение для каждого теста — очищать DOM между тестами, чтобы исключить влияние предыдущих рендеров.
  2. Использование data-testid для стабильного поиска элементов.
  3. Разделение тестов по сценариям — отдельные тесты на рендеринг, события, асинхронные операции.
  4. Проверка реактивности — изменение свойств должно корректно обновлять DOM.
  5. Снимки DOM (snapshot) — можно использовать для быстрого обнаружения неожиданных изменений разметки.

Интеграционные тесты в SkateJS обеспечивают надежность компонентов при взаимодействии с DOM, событиями и другими компонентами. Они позволяют проверять весь жизненный цикл веб-компонентов и гарантируют корректное поведение приложения в целом.