Высокоуровневые компоненты (Higher-Order Components, HOC) в React представляют собой шаблон, который позволяет оборачивать компоненты и передавать им дополнительные пропсы или изменять их поведение. HOC играет важную роль в организации кода и повторном использовании логики. При этом, тестирование HOC может стать достаточно сложным процессом, поскольку они часто скрывают бизнес-логику за абстракциями и делают компонент более универсальным и многократно используемым.
В этом разделе рассмотрим, как можно эффективно протестировать логику HOC с использованием Enzyme, библиотеки для тестирования React-компонентов.
HOC — это функция, которая принимает компонент и возвращает новый компонент с дополнительной логикой. HOC не изменяет сам исходный компонент, а добавляет функциональность, например, обработку состояний, работу с контекстом, добавление стилей или API-запросов.
Пример простого HOC:
const withLoading = (Component) => {
return function WithLoading(props) {
if (props.isLoading) {
return <div>Loading...</div>;
}
return <Component {...props} />;
};
};
Здесь withLoading — это HOC, который принимает компонент
и добавляет логику отображения сообщения “Loading…” до тех пор, пока
isLoading не станет false.
Тестирование HOC можно разделить на несколько этапов. Основные задачи заключаются в проверке работы логики, передаваемой через HOC, а также в тестировании взаимодействия с компонентом, обернутым этим HOC. Важно помнить, что сам HOC, скорее всего, не будет напрямую рендериться в тестах. Вместо этого необходимо проверять, как он влияет на компонент, который является его «подопечным».
Enzyme предоставляет несколько утилит для рендеринга и взаимодействия
с компонентами, таких как shallow, mount и
render. Для тестирования HOC чаще всего используется
shallow, так как он позволяет рендерить компоненты на одном
уровне, избегая рендеринга всех дочерних компонентов.
Пример теста для HOC withLoading:
import React from 'react';
import { shallow } from 'enzyme';
import withLoading from './withLoading';
describe('HOC withLoading', () => {
const DummyComponent = () => <div>Data</div>;
it('should display loading message when isLoading is true', () => {
const WrappedComponent = withLoading(DummyComponent);
const wrapper = shallow(<WrappedComponent isLoading={true} />);
expect(wrapper.text()).toEqual('Loading...');
});
it('should render the wrapped component when isLoading is false', () => {
const WrappedComponent = withLoading(DummyComponent);
const wrapper = shallow(<WrappedComponent isLoading={false} />);
expect(wrapper.find(DummyComponent).length).toBe(1);
});
});
В данном примере:
DummyComponent с
помощью HOC withLoading.isLoading={true} компонент выводит
текст “Loading…”.isLoading={false} обернутый
компонент DummyComponent рендерится корректно.HOC часто изменяет или добавляет пропсы к переданным компонентам. Важно убедиться, что передача пропсов работает как ожидается. Для этого можно воспользоваться Enzyme, чтобы проверить переданные значения.
Пример тестирования пропсов:
import React from 'react';
import { shallow } from 'enzyme';
import withLoading from './withLoading';
const DummyComponent = ({ message }) => <div>{message}</div>;
describe('HOC withLoading with props', () => {
it('should pass correct props to the wrapped component', () => {
const WrappedComponent = withLoading(DummyComponent);
const wrapper = shallow(<WrappedComponent isLoading={false} message="Data loaded" />);
expect(wrapper.find(DummyComponent).props().message).toEqual('Data loaded');
});
});
Здесь мы проверяем, что пропс message передается в
DummyComponent правильно, даже если компонент обернут в
HOC.
В случае, если HOC содержит более сложную логику, например, управление состоянием или асинхронные запросы, тестирование будет включать проверку корректности этих процессов.
Пример HOC с асинхронной логикой:
const withData = (Component) => {
return class WithData extends React.Component {
state = { data: null, isLoading: true };
componentDidMount() {
setTimeout(() => {
this.setState({ data: 'Fetched Data', isLoading: false });
}, 1000);
}
render() {
return <Component {...this.props} data={this.state.data} isLoading={this.state.isLoading} />;
}
};
};
Этот HOC делает асинхронный запрос (в данном случае через
setTimeout) и передает данные в обернутый компонент. Важно
протестировать правильную работу состояния isLoading и
передачу данных.
Пример теста с асинхронной логикой:
import React from 'react';
import { shallow } from 'enzyme';
import withData from './withData';
const DummyComponent = ({ data, isLoading }) => (
<div>
{isLoading ? 'Loading...' : data}
</div>
);
describe('HOC withData', () => {
it('should initially show loading state', () => {
const WrappedComponent = withData(DummyComponent);
const wrapper = shallow(<WrappedComponent />);
expect(wrapper.text()).toEqual('Loading...');
});
it('should show data after loading', () => {
const WrappedComponent = withData(DummyComponent);
const wrapper = shallow(<WrappedComponent />);
// Симулируем ожидание завершения setTimeout
jest.runAllTimers();
wrapper.update();
expect(wrapper.text()).toEqual('Fetched Data');
});
});
В данном примере:
jest.runAllTimers), проверяем, что отображаются полученные
данные.mount для тестирования HOCВ некоторых случаях для тестирования сложных взаимодействий HOC
необходимо использовать mount, который выполняет полный
рендеринг компонента с его дочерними элементами. Это полезно, если вам
нужно проверить взаимодействие с реальным DOM.
Пример использования mount:
import { mount } from 'enzyme';
import withLoading from './withLoading';
describe('HOC withLoading with mount', () => {
const DummyComponent = () => <div>Data</div>;
it('should display loading message with mount', () => {
const WrappedComponent = withLoading(DummyComponent);
const wrapper = mount(<WrappedComponent isLoading={true} />);
expect(wrapper.text()).toEqual('Loading...');
});
});
Тестирование логики HOC требует учета нескольких аспектов: корректности работы обертки, передачи пропсов и взаимодействия с состоянием и асинхронными запросами. Enzyme предоставляет мощные инструменты для рендеринга и взаимодействия с компонентами, что позволяет тщательно проверять все уровни логики HOC.