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

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

Структура приложения на Mithril

Приложения на Mithril обычно строятся вокруг трёх ключевых элементов:

  • Компоненты — функции или объекты с методами view и oninit, отвечающие за рендеринг и управление состоянием.
  • Маршруты — определяют соответствие URL-комбинаций компонентам.
  • Модель данных — объекты и функции, управляющие состоянием приложения и взаимодействием с API.

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

Инструменты для интеграционного тестирования

Для интеграции в среде JavaScript обычно применяются:

  • Mocha или Jest для организации тестов и ассертов.
  • Chai для утверждений.
  • jsdom для имитации DOM в Node.js-среде.
  • Sinon для моков и шпионов, особенно при тестировании API-вызовов.

Тестирование компонентов

Компоненты Mithril можно тестировать, создавая их экземпляры и рендеря в виртуальный DOM:

import m from 'mithril';
import { expect } from 'chai';
import { render } from 'mithril/render';

describe('Component integration', () => {
  it('renders correctly with props', () => {
    const MyComponent = {
      view: ({ attrs }) => m('div', `Hello ${attrs.name}`)
    };
    
    const vnode = m(MyComponent, { name: 'Mithril' });
    const output = render(vnode);
    
    expect(output.outerHTML).to.equal('<div>Hello Mithril</div>');
  });
});

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

Тестирование маршрутов

Mithril предоставляет собственный роутер m.route. Для интеграционных тестов важно проверить:

  • корректность навигации между страницами;
  • правильный рендер компонентов по маршрутам;
  • сохранение состояния при смене URL.

Пример интеграционного теста маршрута:

import m from 'mithril';
import { expect } from 'chai';

describe('Routing integration', () => {
  let root;

  beforeEach(() => {
    root = document.createElement('div');
    document.body.appendChild(root);
  });

  afterEach(() => {
    document.body.removeChild(root);
  });

  it('navigates between routes', () => {
    const Home = { view: () => m('div', 'Home Page') };
    const About = { view: () => m('div', 'About Page') };

    m.route(root, '/', {
      '/': Home,
      '/about': About
    });

    expect(root.textContent).to.equal('Home Page');

    m.route.set('/about');
    expect(root.textContent).to.equal('About Page');
  });
});

Тестирование взаимодействия с API

Для компонентов, работающих с внешними данными, интеграционное тестирование включает имитацию сетевых запросов:

import sinon from 'sinon';
import m from 'mithril';
import { expect } from 'chai';

describe('API integration', () => {
  it('loads data from API', async () => {
    const fakeData = { message: 'Hello' };
    const xhr = sinon.stub(m.request, 'bind').returns(Promise.resolve(fakeData));

    const DataComponent = {
      oninit: vnode => {
        vnode.state.data = null;
        m.request({ method: 'GET', url: '/api/message' }).then(res => {
          vnode.state.data = res.message;
        });
      },
      view: vnode => m('div', vnode.state.data || 'Loading...')
    };

    const root = document.createElement('div');
    m.mount(root, DataComponent);
    await new Promise(resolve => setTimeout(resolve, 0)); // ожидание асинхронного запроса

    expect(root.textContent).to.equal('Hello');
    xhr.restore();
  });
});

Основной принцип — имитация внешних зависимостей для проверки работы приложения в условиях, максимально приближенных к реальной эксплуатации.

Подходы к комплексным тестам

Интеграционные тесты часто объединяют:

  1. Множественные компоненты — проверка взаимодействия между родительскими и дочерними элементами.
  2. Навигацию и маршруты — имитация переходов и проверка состояния.
  3. API и локальное состояние — проверка согласованности данных при асинхронных обновлениях.

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

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

  • Использовать реальный виртуальный DOM для всех тестов, имитирующих взаимодействие пользователя.
  • Избегать чрезмерного мокирования: важнее тестировать реальное взаимодействие компонентов.
  • Разбивать интеграционные тесты на логические сценарии: маршруты, данные, взаимодействие компонентов.
  • Сохранять чистоту состояния между тестами с помощью beforeEach и afterEach.

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