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

Архитектура Kepler.gl строится вокруг связки React-компонентов и Redux-хранилища, где состояние карты, слои, фильтры и визуальные конфигурации строго централизованы. Такой подход определяет структуру тестирования: каждый уровень системы проверяется отдельно, а затем в связке, чтобы исключить расхождения между состоянием, визуализацией и данными.

Система разделяется на несколько ключевых слоёв:

  • UI-компоненты (React) — визуальные элементы управления картой, панели слоёв, фильтры
  • Redux-слой — actions, reducers, selectors, middleware
  • Data processing layer — преобразование данных в формат визуализации
  • Visualization layer — интеграция с WebGL через Mapbox GL
  • Plugins system — расширения функциональности через подключаемые модули

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

Тестирование Redux-слоя (actions и reducers)

Redux-слой в Kepler.gl является центральным элементом управления состоянием, поэтому его тестирование охватывает большую часть логики приложения.

Reducers тестируются как чистые функции, без побочных эффектов:

import visStateReducer from 'kepler.gl/reducers/vis-state';
import {addLayer} from 'kepler.gl/actions';

test('добавление слоя увеличивает количество layers', () => {
  const initialState = {
    layers: []
  };

  const action = addLayer({
    id: 'layer_1',
    type: 'point'
  });

  const state = visStateReducer(initialState, action);

  expect(state.layers.length).toBe(1);
  expect(state.layers[0].id).toBe('layer_1');
});

Actions тестируются через проверку структуры возвращаемого объекта:

import {addFilter} from 'kepler.gl/actions';

test('addFilter формирует корректный action', () => {
  const action = addFilter('layer_1');

  expect(action.type).toBe('ADD_FILTER');
  expect(action.payload.layerId).toBe('layer_1');
});

Особое внимание уделяется:

  • неизменяемости состояния
  • корректности вложенных структур (layers, datasets, filters)
  • предсказуемости редьюсеров при сложных цепочках действий

Тестирование селекторов

Селекторы в Kepler.gl отвечают за вычисление производных данных из состояния Redux. Их тестирование критично для корректной визуализации.

import {getLayers} from 'kepler.gl/selectors';

test('селектор возвращает только активные слои', () => {
  const state = {
    visState: {
      layers: [
        {id: '1', isVisible: true},
        {id: '2', isVisible: false}
      ]
    }
  };

  const result = getLayers(state);

  expect(result.length).toBe(2);
  expect(result.filter(l => l.isVisible).length).toBe(1);
});

Проверяются сценарии:

  • фильтрация активных/неактивных сущностей
  • мемоизация результатов
  • корректная агрегация данных для визуализации
  • устойчивость к неполным состояниям

Тестирование утилит обработки данных

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

import {processCsvData} from 'kepler.gl/processors';

test('CSV преобразуется в dataset с полями', () => {
  const csv = `lat,lng
    10,20
    30,40`;

  const result = processCsvData(csv);

  expect(result.fields).toEqual(
    expect.arrayContaining(['lat', 'lng'])
  );

  expect(result.rows.length).toBe(2);
});

Проверяются кейсы:

  • обработка CSV, JSON, GeoJSON
  • приведение типов (string → number → date)
  • обработка пропущенных значений
  • корректность географических координат
  • производительность при больших объёмах данных

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

React-слой Kepler.gl включает сложные контейнерные компоненты, связанные с Redux и визуализацией.

Используется React Testing Library для проверки поведения компонентов:

import {render} from '@testing-library/react';
import LayerPanel from 'kepler.gl/components/layer-panel';

test('LayerPanel отображает список слоёв', () => {
  const layers = [
    {id: '1', name: 'Layer A'},
    {id: '2', name: 'Layer B'}
  ];

  const {getByText} = render(<LayerPanel layers={layers} />);

  expect(getByText('Layer A')).toBeTruthy();
  expect(getByText('Layer B')).toBeTruthy();
});

Основные аспекты тестирования:

  • корректный рендер при изменении props
  • реакция на пользовательские события (click, drag, input)
  • интеграция с Redux через connect / hooks
  • условный рендеринг сложных UI-веток

Моки Mapbox GL и WebGL окружения

Kepler.gl тесно связан с Mapbox GL, который требует WebGL контекста. В тестовой среде полноценный рендер невозможен, поэтому используется мокирование.

jest.mock('mapbox-gl', () => ({
  Map: jest.fn(() => ({
    on: jest.fn(),
    remove: jest.fn(),
    addLayer: jest.fn()
  }))
}));

При тестировании важно:

  • эмулировать Mapbox события (load, move, zoom)
  • предотвращать реальные WebGL вызовы
  • изолировать визуальный слой от DOM-рендера

Дополнительно используется:

  • mock canvas
  • jsdom окружение
  • stub для requestAnimationFrame

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

Интеграционные тесты проверяют связку Redux → React → Mapbox.

Сценарии включают:

  • добавление слоя и его появление на карте
  • изменение фильтра и обновление визуализации
  • переключение стилей карты
store.dispatch(addLayer({id: 'layer_1'}));

expect(store.getState().visState.layers.length).toBe(1);

Дополнительно проверяется:

  • синхронизация состояния и визуального слоя
  • отсутствие рассинхронизации при быстрых обновлениях
  • корректность обновления данных при batch-операциях

Snapshot-тестирование конфигураций

Snapshot-тестирование применяется для сохранения структуры сложных объектов:

  • конфигурации слоёв
  • стили карт
  • состояние фильтров
import {getStateToSave} from 'kepler.gl/schemas';

test('конфигурация состояния сохраняется стабильно', () => {
  const state = createTestState();

  const saved = getStateToSave(state);

  expect(saved).toMatchSnapshot();
});

Контролируется:

  • стабильность сериализации состояния
  • отсутствие неожиданных изменений структуры
  • совместимость версий конфигураций

Тестирование плагинов и расширений

Плагинная архитектура Kepler.gl позволяет подключать новые типы визуализаций и интерфейсов. Тестирование таких модулей требует проверки контрактов:

  • корректная регистрация плагина
  • совместимость с Redux state shape
  • интеграция с UI registry
test('плагин регистрируется корректно', () => {
  const plugin = createPlugin();

  expect(plugin.id).toBeDefined();
  expect(typeof plugin.render).toBe('function');
});

Особое внимание уделяется:

  • обратной совместимости
  • изоляции плагинов друг от друга
  • корректной обработке отсутствующих зависимостей

Стратегии изоляции состояния

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

Применяются стратегии:

  • reset store перед каждым тестом
  • фабрики состояния (state factories)
  • immutable helpers для копирования state
  • строгий контроль middleware
beforeEach(() => {
  store = configureStore();
});

Также важно исключить:

  • утечки состояния между тестами
  • побочные эффекты асинхронных actions
  • зависимость от порядка выполнения тестов

Типичные проблемы и их устранение

В процессе тестирования Kepler.gl часто возникают характерные сложности:

1. Сложность мокирования WebGL Решается через глубокие стабы Mapbox GL и отключение рендеринга.

2. Большие и вложенные state-объекты Используются фабрики тестовых данных и минимальные состояния.

3. Асинхронные обновления Redux Применяется контроль через fake timers и await dispatch цепочек.

4. Несовместимость snapshot-тестов Стабилизация сериализации и нормализация порядка ключей.

5. Плотная связка UI и state Разделение тестов на презентационные и контейнерные компоненты снижает хрупкость тестов.