Как проверить метод, определенный в функциональном компоненте, который взаимодействует с элементами DOM и не имеет аргументов - PullRequest
1 голос
/ 27 мая 2019

У меня были проблемы с получением 100% тестового покрытия для одной из моих кнопок (функциональные компоненты A React). По сути, при нажатии она выполняет некоторый код, а затем также вызывает другой метод из этого onClick, называемый * 1002.*.Этот метод найдет все кнопки в приложении и удалит класс.Это упреждающее поведение, так что только одна кнопка за раз может быть active.

. До сих пор я проверял щелчок, используя .simulate, передавая имитированный domElement.А затем проверьте, что метод domElement.classList.add вызывается с 'active'.

Очевидно, что это DOM-центрированная операция, и мне очень трудно протестировать метод resetButtons, который находится внутри компонента.особенно учитывая, что у него нет никаких методов.

Я попытался определить метод resetButtons вне компонента, а затем экспортировал его, чтобы его можно было импортировать с помощью jest-теста.Однако я не смог протестировать метод, так как кажется, что он должен быть шпионом или поддельным, а не сам метод.(Matcher error: received value must be a mock or spy function )

Вот реагирующий функциональный компонент:

import React from 'react';
import PropTypes from 'prop-types';
import classes from './MainButton.module.scss';

const MainButton = (props) => {
  const resetButtons = () => {
    const elements = document.getElementsByClassName('mainButton');
    for (let i = 0; i < elements.length; i += 1) {
      elements[i].classList.remove('active');
    }
  };

  const handleClick = (event) => {
    if (!event.target.classList.contains('active')) {
      resetButtons();
      event.target.classList.add('active');
      props.setVisualState(props.className.split('-')[0]);
    }
  };

  return (
    <button
      onClick={handleClick}
      type="button"
      className={`${classes.mainButton} ${props.className}`}
    >
      {props.children}
    </button>
  );
};

MainButton.propTypes = {
  children: PropTypes.node,
  className: PropTypes.string,
  setVisualState: PropTypes.func.isRequired,
};

MainButton.defaultProps = {
  children: 'Button',
  className: '',
};

export default MainButton;

Вот тест

import React from 'react';
import { shallow } from 'enzyme';
import MainButton from './MainButton';

describe('MainButton', () => {
  const domElement = { classList: { contains: jest.fn(), remove: jest.fn(), add: jest.fn() } };
  const setVisualStateMock = jest.fn();
  const mainButton = shallow(<MainButton setVisualState={setVisualStateMock} />);

  it(' is rendered properly', () => {
    expect(mainButton).toMatchSnapshot();
  });

  describe('when clicked', () => {
    beforeEach(() => {
      mainButton.find('button').simulate('click', { target: domElement });
    });

    it('it runs `classlist.add` to assign `active` class', () => {
      expect(domElement.classList.add).toHaveBeenCalledWith('active');
    });

    it('it runs set visual state to update `Allergen` container `state`', () => {
      expect(setVisualStateMock).toHaveBeenCalled();
    });
  });
});

В настоящее время отчет о покрытии сообщает о 92% покрытии,но ветвь в 50, и линия, которая вызывает проблему, находится в строке 9 (строка elements[i].classList.remove('active');.

Я знаю, что на 90% я, вероятно, должен просто идти дальше, но это то, чем я хочу бытьв состоянии разобраться. Чувствую, что обдумать это сделает меня лучше.

Надеюсь, вы, ребята, можете помочь!

Ответы [ 2 ]

1 голос
/ 27 мая 2019

Вы должны иметь возможность смонтировать несколько MainButton s, щелкнуть один и ожидать, что другим (им) был вызван domElement.classList.remove.

Однако пользователь konqi прав в том, что React обеспечивает лучшеспособы манипулирования элементами / компонентами.

Вы можете заменить этот тест:

expect(domElement.classList.add).toHaveBeenCalledWith('active');

тестом, который проверяет, имеет ли кнопка (или не имеет) active className (вместопроверки, что функция была вызвана с правильным аргументом).С этим тестом, если хотите, вы можете изменить его так, как предлагает Конки.

1 голос
/ 27 мая 2019

Суетиться в DOM самостоятельно - это анти-паттерн.Это работа Реакта.Вместо того, чтобы манипулировать dom с помощью target.classList.add, у вас должно быть свойство состояния, которое хранит статус того, какой из ваших входов активен в данный момент.Затем при рендеринге вы можете сказать className={isActiveInput ? "active": null}.

Поскольку состояние не является специфическим для вашего компонента MainButton, вы бы подняли состояние до .Если у вас есть состояние где-то в родительском элементе, вам не нужно грубо искать элементы DOM по имени класса и манипулировать dom самостоятельно.

Проще говоря, правило React таково: вы определяете, как вещи должныпохоже, React позаботится о том, чтобы ваше определение стало реальностью в доме.Если вы сами манипулируете DOM - вы делаете это неправильно.

Когда все это будет сделано, у вас не возникнет никаких проблем с тестами, потому что все, что вам нужно сделать, это обеспечить надлежащее состояние и реквизиты., что легко, и убедитесь, что ваш обратный вызов запущен на клике.

РЕДАКТИРОВАТЬ: расширенная версия будет использовать Context , но я бы сначала поднял состояние.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...