Каждый рабочий день я пишу множество тестов с помощью Enzyme. Несмотря на то, что библиотека хорошо спроектирована, есть несколько хитрых моментов, когда легко ошибиться и потратить много драгоценного времени, пытаясь выяснить, почему один тест не проходит… 😤

Обновление состояния onEvent

Рассмотрим простой сценарий:

  • монтируем компонент и получаем обертку
  • prop.value из tab имеет исходное значение before
  • смоделировано click событие
  • prop.value должно иметь значение after
const tab = wrapper.find('.tab');
expect(tab.props().value).toEqual('before');
tab.find('button').simulate('click');
expect(tab.props().value).toEqual('after');

Выглядит неплохо, не правда ли? К сожалению, это может не сработать 🥴

Далее проблема описана в официальном трекере проблем Enzyme.

TL; DR: оболочка не обновляет свои значения сама по себе - нам нужно запускать обновление вручную. Более того, уже найденные обертки не обновляются. Это означает, что нужно добавить две строки кода, чтобы тест стал зеленым:

...
tab.find('button').simulate('click');
wrapper.update();
tab = wrapper.find('tab');
expect(tab.props().value).toEqual('after');

Пример из реальной жизни

Есть две кнопки, изначально активна первая. После нажатия на другую, вторая кнопка становится активной.

function findButtons(wrapper: ReactWrapper) {
    const buttons = wrapper.find(buttonClass);
    return { first: buttons.first(), last: buttons.last() };
}
it('should select active button when clicked', () => {
    const panel = mount({ buttons, activeIndex: 0 });
    expect(findButtons(panel).first.find(Styles.active)).exists())
        .toBeTruthy();
    expect(findButtons(panel).last.find(Styles.active)).exists())
        .toBeFalsy();

    findButtons(panel).last.simulate(EVENT_CLICK);
    panel.update();
    expect(findButtons(panel).first.find(Styles.active)).exists())
        .toBeFalsy();
    expect(findButtons(panel).last.find(Styles.active)).exists())
        .toBeTruthy();
});

Моделирование различных типов событий

Выше было описано событие клика. Однако есть много других событий, например:

  • change
  • blur
  • focus

Может случиться так, что в нашем приложении есть textarea. Для нашего варианта использования представьте, что длина текста, который может писать пользователь, ограничена. Нам нужен тест, который проверяет, правильно ли работает наша логика и textarea получает красную рамку, когда набирается слишком много букв.

Как мы можем установить текст в textarea?

wrapper.find('textarea').simulate('click');

Очевидно, ничего не выйдет - чтобы изменить текст, нам нужно событие change, а не просто click 🤭 Так что, возможно:

wrapper.find('textarea').simulate('change');

Неа. Мы должны передать текстовое значение для отображения 💡 Должно быть указано событие!

const event = { target: { value: 'comment '} };
wrapper.find('textarea').simulate('change', event);

Вывод: simulate принимает два параметра - произвольное название события и его значение.

Резюме

Реагировать + Фермент = 🌩️❤️🌩️

Спасибо за чтение и оставайтесь на связи! 👏👏👏