Hyperapp строится вокруг чистых функций состояния и действий, что делает его особенно удобным для модульного тестирования. В архитектуре Hyperapp есть три ключевых элемента: state (состояние), actions (действия) и effects (эффекты). Каждый из них можно тестировать независимо, что позволяет создавать надёжные и предсказуемые приложения.
Действия в Hyperapp — это функции, которые принимают текущее состояние и возвращают новое состояние либо объект с эффектами. Ключевая особенность для тестирования — чистота функций.
const increment = state => ({ ...state, count: state.count + 1 });
Для тестирования такой функции используется прямое сравнение результатов:
const state = { count: 0 };
const nextState = increment(state);
console.assert(nextState.count === 1, "Count должен увеличиться на 1");
Если действие вызывает другое действие или возвращает объект с несколькими ключами, важно проверять структуру возвращаемого значения:
const addTodo = (state, title) => ({
todos: [...state.todos, { title, done: false }],
});
const state = { todos: [] };
const nextState = addTodo(state, "Купить хлеб");
console.assert(nextState.todos.length === 1, "Должен появиться новый элемент списка");
console.assert(nextState.todos[0].title === "Купить хлеб", "Заголовок должен совпадать");
Hyperapp предполагает, что действия не мутируют исходный объект состояния:
console.assert(state.todos.length === 0, "Исходное состояние не должно изменяться");
Эффекты в Hyperapp предназначены для взаимодействия с внешним миром: сетевые запросы, таймеры, LocalStorage и т.д. Эффекты не должны напрямую менять состояние, поэтому их тестирование отличается от тестирования действий.
const logEffect = message => (dispatch, props) => {
console.log(props.message);
};
Для тестов эффекты часто заменяют фейковыми функциями, чтобы избежать реального воздействия на систему:
let logged = null;
const mockEffect = message => (dispatch, props) => {
logged = props.message;
};
mockEffect("Hello")(null, { message: "Hello" });
console.assert(logged === "Hello", "Сообщение должно быть записано через эффект");
Если действие возвращает объект с эффектом, тестирование выглядит так:
const showMessage = state => [
state,
logEffect("Привет!")
];
const [nextState, effect] = showMessage({});
let messageLogged = null;
effect(null, { message: "Привет!" });
console.assert(messageLogged === null, "В тесте можно перехватывать эффект");
Иногда требуется проверить, что действие корректно вызывает эффект с нужными параметрами:
const fetchData = () => (state) => [
state,
(dispatch, props) => {
dispatch({ type: "dataLoaded", data: [1, 2, 3] });
}
];
let dispatched = null;
const mockDispatch = action => { dispatched = action; };
const [newState, effect] = fetchData()({});
effect(mockDispatch);
console.assert(dispatched.type === "dataLoaded", "Должен быть вызван action dataLoaded");
console.assert(Array.isArray(dispatched.data), "Данные должны быть массивом");
Для автоматизации тестов в Hyperapp можно использовать Jest, Mocha или Vitest. Они позволяют проверять:
Пример с Jest:
test("increment увеличивает count", () => {
const state = { count: 0 };
const nextState = increment(state);
expect(nextState.count).toBe(1);
expect(state.count).toBe(0);
});
Hyperapp идеально подходит для разработки через тестирование (TDD). Сначала пишется тест для действия или эффекта, затем реализуется функция. Такой подход гарантирует:
Тесты становятся не вспомогательным инструментом, а неотъемлемой частью архитектуры, обеспечивая стабильность и масштабируемость приложений.