Unit тестирование с Jest и Mocha

Unit-тестирование — важная часть разработки на Riot.js, позволяющая проверять отдельные компоненты и их поведение в изоляции. В экосистеме JavaScript для этих целей чаще всего используют Jest и Mocha, которые обеспечивают гибкие возможности для написания тестов, проверки асинхронного кода и мокирования зависимостей.


Настройка окружения для тестирования Riot-компонентов

Для начала необходимо установить основные зависимости:

npm install --save-dev jest @testing-library/jest-dom @testing-library/dom @riotjs/riot
npm install --save-dev mocha chai sinon
  • Jest — фреймворк для тестирования с встроенным запуском, моками и покрытием кода.
  • Mocha — гибкий тестовый раннер, позволяющий использовать различные библиотеки assertion (chai) и mocks (sinon).

Riot-компоненты представляют собой веб-компоненты с внутренней логикой, шаблонами и состоянием. Для их тестирования необходимо интегрировать библиотеку @riotjs/riot с DOM-тестовой библиотекой, такой как @testing-library/dom.


Структура теста компонента

Типичная структура теста выглядит следующим образом:

  1. Импорт компонента
  2. Монтирование компонента
  3. Проверка DOM и состояния
  4. Тестирование событий и реакций

Пример на Jest:

import { component } from '@riotjs/riot'
import { render, fireEvent, screen } from '@testing-library/dom'
import Counter from './counter.riot'

describe('Counter компонент', () => {
  let root

  beforeEach(() => {
    root = document.createElement('div')
    render(Counter, {}, root)
  })

  it('должен отображать начальное значение', () => {
    expect(root.textContent).toContain('0')
  })

  it('увеличивает значение при клике на кнопку', async () => {
    const button = screen.getByText('Increment')
    await fireEvent.click(button)
    expect(root.textContent).toContain('1')
  })
})

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

  • render монтирует компонент в DOM, позволяя проверять его визуальное состояние.
  • fireEvent симулирует события пользователя.
  • Проверка происходит через стандартные DOM-assertions, поддерживаемые Jest.

Тестирование с Mocha и Chai

Mocha позволяет создавать более гибкую структуру тестов и интегрироваться с Sinon для мокирования:

import { component } from '@riotjs/riot'
import { expect } from 'chai'
import sinon from 'sinon'
import Counter from './counter.riot'

describe('Counter компонент', () => {
  let root

  beforeEach(() => {
    root = document.createElement('div')
    component(Counter)(root)
  })

  it('инициализируется с нулевым значением', () => {
    expect(root.textContent).to.include('0')
  })

  it('увеличивает счет при вызове handleIncrement', () => {
    const instance = root._riot
    instance.handleIncrement()
    expect(root.textContent).to.include('1')
  })
})

Отличительные особенности Mocha:

  • Разделение тестов на describe и it с понятной структурой.
  • Гибкое использование beforeEach и afterEach для подготовки состояния.
  • Возможность мокирования функций и времени через sinon.

Асинхронные действия и события

Riot-компоненты часто используют асинхронные операции: запросы к API, таймеры, анимации. Для их тестирования важно правильно обрабатывать промисы и асинхронные события.

Пример с Jest:

it('загружает данные из API', async () => {
  const fetchMock = jest.spyOn(global, 'fetch').mockResolvedValue({
    json: () => Promise.resolve({ value: 42 })
  })

  render(Counter, {}, root)
  await Promise.resolve() // ожидание асинхронного обновления компонента
  expect(root.textContent).toContain('42')
  fetchMock.mockRestore()
})

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

  • jest.spyOn позволяет перехватывать вызовы глобальных функций.
  • Асинхронные обновления DOM проверяются через await и ожидание промисов.

Мокирование и изоляция зависимостей

Unit-тестирование требует изоляции компонентов от внешних сервисов. Riot-компоненты могут зависеть от других компонентов, сервисов или глобальных объектов.

Пример с Mocha + Sinon:

it('вызывает метод сервиса при событии', () => {
  const service = { save: sinon.spy() }
  const instance = component(Counter, { service })(root)

  instance.handleSave()
  expect(service.save.calledOnce).to.be.true
})

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

  • Внедрение зависимостей через параметры компонента позволяет легко заменять их моками.
  • Sinon предоставляет методы spy, stub, mock для точной проверки вызовов.

Покрытие и интеграция с CI

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

jest --coverage
  • Отчет показывает, какие строки и ветвления кода не проверены.
  • Интеграция с CI/CD (GitHub Actions, GitLab CI) позволяет автоматически запускать тесты при каждом коммите.

Практические рекомендации

  • Разделять unit-тесты и интеграционные тесты: unit-тесты проверяют логику отдельного компонента, интеграционные — взаимодействие нескольких компонентов.
  • Использовать моки и spies для внешних зависимостей, чтобы тест оставался быстрым и предсказуемым.
  • Проверять не только DOM, но и внутреннее состояние компонентов, особенно реактивные свойства Riot.js.
  • Асинхронные действия тестировать через async/await и промисы, чтобы избежать гонок и ошибок тайминга.

Unit-тестирование в Riot.js с Jest и Mocha обеспечивает высокое качество компонентов и упрощает поддержку приложения. Сочетание тестовых раннеров, библиотек для проверки DOM и инструментов мокирования позволяет писать надежные и изолированные тесты для любых сценариев.