Как должен выглядеть простой интеграционный тест с Enzyme и хуком useContext?
Уважаемые участники. Помогите написать простой интеграционный тест для компонента на React (компонент использует хук useContext). Тест должен проверять, что были нажаты кнопки и были вызваны обработчики (код: https://codesandbox.io/s/lingering-violet-n11hu).
Код компонента, который проверяет тест:
import React, {useContext} from "react";
import {StoreContext} from "../../reducer/context";
import moment from "moment";
import Delay from "../delay/delay";
let queue = Promise.resolve();
const Interface = () => {
const {state, dispatch} = useContext(StoreContext);
const handleLogSet = e => {
const timeout = parseInt(e.target.getAttribute("data-delay"), 10);
const timePress = moment().format("LTS");
queue = queue.then(() => Delay(timeout, timePress)).then(res => dispatch({
type: "SET_LOG", payload: "\n" + res
}));
};
const handleReset = () => {
dispatch({type: "RESET"});
};
return (
<div className="block">
<button className="btn" data-delay="1" onClick={handleLogSet}>Кнопка 1</button>
<button className="btn" data-delay="2" onClick={handleLogSet}>Кнопка 2</button>
<button className="btn" data-delay="3" onClick={handleLogSet}>Кнопка 3</button>
<button className="btn" onClick={handleReset}>Reset</button>
<textarea value={state.join("")} readOnly={true}/>
</div>
);
};
export default Interface;
Пробовал разные варианты теста, но ни один не работает. Пробовал, например так:
import {configure, shallow } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import Interface from "./interface";
import React, { useContext } from "react";
import { StoreContext } from "../../reducer/context";
configure({ adapter: new Adapter() });
const { state } = useContext(StoreContext);
it(`Click by button calls callback`, () => {
const handleLogSet = jest.fn();
const component = shallow(<Interface
state={state}
/>);
component.find(`.button`).simulate(`click`);
expect(handleLogSet).toHaveBeenCalledTimes(1);
});
Выдавались разные ошибки, в том числе, такие: "Invalid hook call. Hooks can only be called inside of the body of a function component".
Буду очень благодарен за пример рабочего кода и краткого пояснения. Всем огромное спасибо!
Ответы (1 шт):
Итак, все было достаточно просто. Стоит заметить, что при тестировании компонентов, которые используют useContext при помощи метода shallow библиотеки Enzyme существуют признанные сложности. Решить их напрямую пока что не получается.
Первое, что нужно сделать - это создать пользовательский хук. Сделать это можно примерно вот так.
import React, {useContext} from 'react';
export const useAppContext = () => useContext(AppContext);
const AppContext = React.createContext();
export default AppContext;
Делается это с целью не использовать useContext напрямую в тестируемом компоненте.
Сам же e2e тест выглядит примерно так:
import React from "react";
import {configure, shallow} from "enzyme";
import * as AppContext from "../../reducer/context";
import Adapter from "enzyme-adapter-react-16";
import Interface from "./interface";
configure({adapter: new Adapter()});
it(`Click by Set and Reset buttons calls callback`, () => {
const contextValues = {state: ["Моковый стейт"]};
const handleReset = jest.fn();
const handleLogSet = jest.fn();
jest
.spyOn(AppContext, "useAppContext")
.mockImplementation(() => contextValues);
const wrapper = shallow(
<Interface
onReset={handleReset}
onLogSet={handleLogSet}
/>
);
wrapper.find(`.block__btn--reset`).simulate(`click`);
expect(handleReset).toHaveBeenCalledTimes(1);
wrapper.find(`.block__btn--set`).forEach(item => {
item.simulate(`click`);
expect(handleReset).toHaveBeenCalledTimes(1);
});
});
Таким образом, мы имитируем реализацию нашего пользовательского хука и передаем это значение в объект контекста.