Архитектура Kepler.gl строится вокруг связки React-компонентов и Redux-хранилища, где состояние карты, слои, фильтры и визуальные конфигурации строго централизованы. Такой подход определяет структуру тестирования: каждый уровень системы проверяется отдельно, а затем в связке, чтобы исключить расхождения между состоянием, визуализацией и данными.
Система разделяется на несколько ключевых слоёв:
Тестирование строится по принципу изоляции: каждый слой проверяется независимо, с минимальными зависимостями от внешнего окружения.
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');
});
Особое внимание уделяется:
Селекторы в 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);
});
Проверяются кейсы:
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();
});
Основные аспекты тестирования:
Kepler.gl тесно связан с Mapbox GL, который требует WebGL контекста. В тестовой среде полноценный рендер невозможен, поэтому используется мокирование.
jest.mock('mapbox-gl', () => ({
Map: jest.fn(() => ({
on: jest.fn(),
remove: jest.fn(),
addLayer: jest.fn()
}))
}));
При тестировании важно:
Дополнительно используется:
Интеграционные тесты проверяют связку Redux → React → Mapbox.
Сценарии включают:
store.dispatch(addLayer({id: 'layer_1'}));
expect(store.getState().visState.layers.length).toBe(1);
Дополнительно проверяется:
Snapshot-тестирование применяется для сохранения структуры сложных объектов:
import {getStateToSave} from 'kepler.gl/schemas';
test('конфигурация состояния сохраняется стабильно', () => {
const state = createTestState();
const saved = getStateToSave(state);
expect(saved).toMatchSnapshot();
});
Контролируется:
Плагинная архитектура Kepler.gl позволяет подключать новые типы визуализаций и интерфейсов. Тестирование таких модулей требует проверки контрактов:
test('плагин регистрируется корректно', () => {
const plugin = createPlugin();
expect(plugin.id).toBeDefined();
expect(typeof plugin.render).toBe('function');
});
Особое внимание уделяется:
Kepler.gl активно использует глобальное состояние, поэтому важным аспектом тестирования является его изоляция между тестами.
Применяются стратегии:
beforeEach(() => {
store = configureStore();
});
Также важно исключить:
В процессе тестирования Kepler.gl часто возникают характерные сложности:
1. Сложность мокирования WebGL Решается через глубокие стабы Mapbox GL и отключение рендеринга.
2. Большие и вложенные state-объекты Используются фабрики тестовых данных и минимальные состояния.
3. Асинхронные обновления Redux Применяется контроль через fake timers и await dispatch цепочек.
4. Несовместимость snapshot-тестов Стабилизация сериализации и нормализация порядка ключей.
5. Плотная связка UI и state Разделение тестов на презентационные и контейнерные компоненты снижает хрупкость тестов.