Тестирование прослушивания кликов с помощью Enzyme - PullRequest
0 голосов
/ 07 июня 2019

У меня есть компонент с состоянием, который подключает прослушиватель событий dom при монтировании. Если пользователь щелкает по данному элементу, тогда другой данный элемент будет условно появляться и исчезать. Я хочу написать тест для этого, но когда я делаю это с использованием фермента, я получаю сообщение об ошибке:

sampleComponent.js:

import React from 'react';

class SampleComponent extends React.Component {
  constructor() {
    super();
    this.state = {
      onClick: false,
    };
    this.handleClick = this.handleClick.bind(this);
  }

  componentDidMount() {
    document.addEventListener('mousedown', this.handleClick);
  }

  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClick);
  }

  handleClick(event) {
    if (this.divRef && this.divRef.contains(event.target)) {
      this.setState(prevState => ({ onClick: !prevState.onClick }));
    }
  }

  render() {
    return (
      <div
        ref={(node) => { this.divRef = node; }}
        test-attr="div"
      >
        {
          this.state.onClick && <p test-attr="p">clicked!</p>
        }
      </div>
    );
  }
}

export default SampleComponent;

sampleComponent.test.js:

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

test('renders component without errors', () => {
  const wrapper = shallow(<SampleComponent />);
  const div = wrapper.find('[test-attr="div"]');
  const p = wrapper.find('[test-attr="p"]');
  div.simulate('click');
  expect(p.length).toEqual(1);
});

Ошибка:

Error: 
expect(received).toEqual(expected)

Expected value to equal:   
   1 
Received:   
   0 
Expected :1
Actual   :0

Почему симуляция щелчка не обновляет должным образом состояние моего компонента? Спасибо!

1 Ответ

2 голосов
/ 10 июня 2019

По своей структуре энзим не поддерживает прослушиватели событий, поскольку они являются реализацией Javascript, а не реализацией React .Таким образом, вам придется выполнить некоторые хитрости Javascript и Jest, чтобы подражать слушателю событий.

В этом случае вам действительно не нужно тестировать обработчик событий, поскольку вы просто манипулируете состоянием.Обойдя прослушиватель событий, вы можете вручную манипулировать свойством класса onClick и утверждать, как соответствующим образом изменяются состояние и DOM - это будет более ориентированный на React тест.Однако даже это немного усложняет задачу, поскольку onClick ожидает настоящий узел DOM.Таким образом, еще более простой подход состоит в том, чтобы просто манипулировать состоянием непосредственно с помощью wrapper.setState({ ... }) и делать утверждения против изменений DOM.

В дополнение к этому, я предпочитаю использовать className s вместо data-attributes, поскольку ониболее полезны для стилей и тестирования , и они не загрязняют DOM множеством ненужных и / или неиспользуемых свойств.

Приведенный ниже пример охватывает все3 варианта.

  • ClickHandlerEvent.test.js (имитировать событие)
  • ClickHandlerHandleClick.test.js (имитировать handleClick)
  • ClickHandler.test.js (манипулировать состоянием)

Рабочий пример (нажмите вкладку Tests - расположена справа от Browser - для запуска всех тестов):

Edit Click Listener Testing


компонентов / ClickHandler / index.js (компонент)

import React, { Fragment, Component } from "react";
import ClickBox from "../ClickBox";

class ClickHandler extends Component {
  state = {
    isVisible: false
  };

  componentDidMount = () => {
    document.addEventListener("mousedown", this.handleClick);
  };

  componentWillUnmount = () => {
    document.removeEventListener("mousedown", this.handleClick);
  };

  handleClick = ({ target }) => {
    this.setState({
      isVisible: this.wrapperRef && this.wrapperRef.contains(target)
    });
  };

  render = () => {
    const { isVisible } = this.state;

    return (
      <div className="wrapper" ref={node => (this.wrapperRef = node)}>
        <ClickBox>
          <p className="instruction">
            (click <strong>{isVisible ? "outside" : "inside"}</strong> the box
            to <strong>{isVisible ? "hide" : "show"}</strong> the message)
          </p>
          <h2 className="message">
            {isVisible ? (
              <Fragment>
                Hello <strong>World</strong>!
              </Fragment>
            ) : null}
          </h2>
        </ClickBox>
      </div>
    );
  };
}

export default ClickHandler;

Вариант 1

компонентов / ClickHandler / __ tests __ / ClickHandlerEvent.test.js (имитировать событие)

import React, { Fragment } from "react";
import { mount } from "enzyme";
import ClickHandler from "../index";

const initialState = {
  isVisible: false
};

// elevating the event listener to the test
const eventListener = {};
document.addEventListener = (evt, cb) => (eventListener[evt] = cb);

describe("Click Handler", () => {
  let wrapper;
  beforeAll(() => {
    wrapper = mount(
      <Fragment>
        <ClickHandler />
        <div className="outside" />
      </Fragment>
    );
    wrapper.setState({ ...initialState });
  });

  afterAll(() => {
    wrapper.unmount();
  });

  it("renders without errors and the message should be hidden", () => {
    expect(wrapper.find("div.wrapper")).toHaveLength(1);
    expect(wrapper.find("h2.message").text()).toEqual("");
  });

  it("displays a message when a click is inside of the box", () => {
    // manually triggering the event listener with a node 
    // inside of "ClickHandler"
    eventListener.mousedown({
      target: wrapper
        .find("ClickHandler")
        .getDOMNode()
        .getElementsByClassName("instruction")[0]
    });

    expect(wrapper.find("ClickHandler").state("isVisible")).toBeTruthy();
    expect(wrapper.find("h2.message").text()).toEqual("Hello World!");
  });

  it("hides the message when the click is outside of the box", () => {
    // manually triggering the event listener with a node
    // outside of "ClickHandler"
    eventListener.mousedown({
      target: wrapper.find("div.outside").getDOMNode()
    });

    expect(wrapper.find("ClickHandler").state("isVisible")).toBeFalsy();
    expect(wrapper.find("h2.message").text()).toEqual("");
  });
});

Опция 2

Компоненты / ClickHandler / __ tests __ / ClickHandlerHandleClick.test.js (мнемосхема handleClick)

import React, { Fragment } from "react";
import { mount } from "enzyme";
import ClickHandler from "../index";

const initialState = {
  isVisible: false
};

describe("Click Handler", () => {
  let wrapper;
  beforeAll(() => {
    wrapper = mount(
      <Fragment>
        <ClickHandler />
        <div className="outside" />
      </Fragment>
    );
    wrapper.setState({ ...initialState });
  });

  afterAll(() => {
    wrapper.unmount();
  });

  it("renders without errors and the message should be hidden", () => {
    expect(wrapper.find("div.wrapper")).toHaveLength(1);
    expect(wrapper.find("h2.message").text()).toEqual("");
  });

  it("displays a message when a click is inside of the box", () => {
    // manually triggering the handleClick class property with a 
    // node inside of "ClickHandler"
    wrapper
      .find("ClickHandler")
      .instance()
      .handleClick({
        target: wrapper
          .find("ClickHandler")
          .getDOMNode()
          .getElementsByClassName("instruction")[0]
      });

    expect(wrapper.find("ClickHandler").state("isVisible")).toBeTruthy();
    expect(wrapper.find("h2.message").text()).toEqual("Hello World!");
  });

  it("hides the message when the click is outside of the box", () => {
    // manually triggering the handleClick class property with a 
    // node outside of "ClickHandler"
    wrapper
      .find("ClickHandler")
      .instance()
      .handleClick({
        target: wrapper.find("div.outside").getDOMNode()
      });

 expect(wrapper.find("ClickHandler").state("isVisible")).toBeFalsy();
    expect(wrapper.find("h2.message").text()).toEqual("");
  });
});

Опция 3

компонентов / ClickHandler / __ tests __ / ClickHandler.test.js (манипулировать состоянием)

import React from "react";
import { mount } from "enzyme";
import ClickHandler from "../index";

const initialState = {
  isVisible: false
};

describe("Click Handler", () => {
  let wrapper;
  beforeAll(() => {
    wrapper = mount(<ClickHandler />);
    wrapper.setState({ ...initialState });
  });

  afterAll(() => {
    wrapper.unmount();
  });

  it("renders without errors and the message should be hidden", () => {
    expect(wrapper.find("div.wrapper")).toHaveLength(1);
    expect(wrapper.find("h2.message").text()).toEqual("");
  });

  it("displays a message when a click is inside of the box", () => {
    // manually manipulating state
    wrapper.setState({ isVisible: true });
    expect(wrapper.find("h2.message").text()).toEqual("Hello World!");
  });

  it("hides the message when the click is outside of the box", () => {
    // manually manipulating state
    wrapper.setState({ isVisible: false });
    expect(wrapper.find("h2.message").text()).toEqual("");
  });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...