Как должен выглядеть простой интеграционный тест с перехватчиками Enzyme и useContext? - PullRequest
0 голосов
/ 06 мая 2020

Помогите написать простой интеграционный тест для компонента в 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);
});

Были выданы различные ошибки, в том числе следующие: «Неверный вызов ловушки. Перехватчики могут быть вызваны только внутри тела функционального компонента».

Буду очень признателен за пример рабочего кода и краткое пояснение. Всем большое спасибо!

1 Ответ

0 голосов
/ 09 мая 2020

Так что все было достаточно просто. Стоит отметить, что при тестировании компонентов, использующих useContext, с использованием поверхностного метода библиотеки 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: ["Mock 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);
    });
});

Таким образом, мы имитируем c реализацию нашего пользовательского кода и передаем это значение объекту контекста.

...