Почему функция Enzyme `find()` неверно возвращает несуществующие элементы?

#javascript #reactjs #jestjs #enzyme

#javascript #reactjs #jestjs #фермент

Вопрос:

Ниже у меня есть пример компонента React, который ведет себя следующим образом —

  • Когда showDiv состояние false (начальное состояние), оно ничего не отображает.
  • Когда showDiv состояние обновляется true (возможно, с помощью какого-либо внешнего компонента, redux и т. Д.), Оно отображает a <div> с помощью одного радиовхода.

Я также включил тесты Jest:

 import { expect } from 'chai';
import { mount } from 'enzyme';
import PropTypes from 'prop-types';
import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { showDiv: false };
  }

  render() {
    const { showDiv } = this.state;

    if (!showDiv) { return null; }

    return (
      <div className='my-component'>
        <input type="radio" value="yes" id="foo" checked={true} />
      </div>
    );
  }
}


describe('<MyComponent />', () => {
  let wrapper;

  beforeEach(() => {
    wrapper = mount(<MyComponent />);
  });

  it('should render the div', () => {
    // This INCORRECTLY passes
    expect(wrapper.find('#foo').at(0)).to.not.be.null;
    expect(wrapper.find('#foo').at(0)).to.be.checked;
  });
});

 

Я написал тест, чтобы убедиться, что <input> он существует и что он есть checked , и тест пройден.

Однако при первоначальном монтировании / загрузке компонента это <input> не должно отображаться (поскольку showDiv есть false ), так почему тест проходит?

Я даже вывожу состояние компонента с console.log(wrapper.find('#foo').at(0).debug()); помощью и могу убедиться, что ничего не отображается… но он все равно проходит.

 $ npx jest --no-cache --config test/js/jest.config.js test/js/components/test.spec.jsxConsole

    console.log test/js/components/test.spec.jsx:34
 

Есть ли причина, по которой это дает ложное срабатывание? В частности, почему find() возвращает элементы, которые не существуют, но все же проходят проверки (например .to.be.checked )

Комментарии:

1. Может ли быть проблема с индексацией и / или проблема с управлением памятью низкого уровня? Я не знаком с «Enzyme», это просто «общие предположения»…

Ответ №1:

Я лично никогда не работал с chai , но это может потребовать некоторой дополнительной настройки для работы React (я знаю, что для jest с enzyme требуется адаптер). На той же ноте я бы лично рекомендовал просто использовать Jest поверх чая, так как он уже завернут поверх энзима… но в конечном итоге зависит от ваших предпочтений, какой набор тестов использовать.

Примечание: популярным дополнением к jest является jest-enzyme, в котором есть такие методы утверждения, как toExist() и toBeChecked() .

Рабочая демонстрация с использованием Jest (переключение с Browser вкладки на Tests вкладку для выполнения утверждений):

Редактировать Шутку с тестированием на ферменты


App.test.js

 import React from "react";
import { configure, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import MyComponent from "./App";

configure({ adapter: new Adapter() });

describe("<MyComponent />", () => {
  let wrapper;

  beforeEach(() => {
    wrapper = mount(<MyComponent />);
  });

  it("initially displays a button", () => {
    expect(wrapper.find("#show-radio")).toHaveLength(1);
  });

  it("renders the 'my-component' div", () => {
    wrapper.find("#show-radio").simulate("click");

    expect(wrapper.find(".my-component")).toHaveLength(1);
    expect(wrapper.find("#foo").props().checked).toBeFalsy();
  });

  it("selects a radio", () => {
    wrapper.find("#show-radio").simulate("click");
    wrapper.find("#foo").prop("onChange")({ target: { value: "1" } });
    wrapper.update();

    expect(wrapper.find("#foo").props().checked).toBeTruthy();
  });

  it("resets radio value", () => {
    wrapper.find("#show-radio").simulate("click");
    wrapper.find("#foo").prop("onChange")({ target: { value: "1" } });
    wrapper.find("#reset").simulate("click");

    wrapper.update();

    expect(wrapper.find("#foo").props().checked).toBeFalsy();
  });

  it("hides radios", () => {
    wrapper.find("#show-radio").simulate("click");
    wrapper.find("#close-radio").simulate("click");

    wrapper.update();

    expect(wrapper.find("#show-radio")).toHaveLength(1);
    expect(wrapper.find(".my-component")).toHaveLength(0);
  });
});
 

App.js

 import React from "react";
import "./styles.css";

export default class MyComponent extends React.Component {
  state = { showDiv: false, value: 0 };

  setValue = ({ target: { value } }) => this.setState({ value });

  resetValue = () => this.setState({ value: 0 });

  toggleRadio = () =>
    this.setState((prevState) => ({ showDiv: !prevState.showDiv }));

  render() {
    const { showDiv, value } = this.state;

    if (!showDiv) {
      return (
        <button id="show-radio" type="button" onClick={this.toggleRadio}>
          Toggle
        </button>
      );
    }

    return (
      <div className="my-component">
        <label htmlFor="foo">
          <input
            type="radio"
            id="foo"
            value="1"
            checked={value === "1"}
            onChange={this.setValue}
          />
          1
        </label>
        <label htmlFor="bar">
          <input
            type="radio"
            id="bar"
            value="2"
            checked={value === "2"}
            onChange={this.setValue}
          />
          2
        </label>
        <br />
        <button id="reset" type="button" onClick={this.resetValue}>
          Reset
        </button>
        <button id="close-radio" type="button" onClick={this.toggleRadio}>
          Close
        </button>
      </div>
    );
  }
}