繁体   English   中英

React 测试库适用于 HTML 个元素,但不适用于 React 组件

[英]React Testing Library works on HTML elements but not with React components

基本上,我正在尝试测试具有 select 的组件。

尝试测试组件时,测试失败,返回的是默认值而不是更改后的值。

但是,当我使用渲染组件的 HTML(来自 screen.debug())时,它会起作用。

组件:

export function SelectFile({
  fileList,
  handleChange,
  selected,
}) {
  return (
    <select
      className="bg-slate-600 rounded w-auto"
      onChange={onChange}
      value={selected}
    >
      <option value="">Select an option</option>
      <TodayOptions />
      <AllOptions />
    </select>
  );

  function AllOptions() {
    return (
      <>
        {Object.entries(groups).map(([key, value]) => {
          return (
            <optgroup key={key} label={key.toLocaleUpperCase()}>
              {[...value].sort(sortByDateFromLogs).map((item) => (
                <option key={item} value={item}>
                  {item}
                </option>
              ))}
            </optgroup>
          );
        })}
      </>
    );
  }

  function TodayOptions() {
    const todayFiles = Object.values(groups)
      .map((group) => {
        const today = new Date().toLocaleDateString().replace(/\//g, '-');
        return group.filter((file) => file.includes(today));
      })
      .flat();

    if (todayFiles.length === 0) {
      return null;
    }

    return (
      <optgroup label="Today">
        {todayFiles.map((item) => (
          <option key={item}>{item}</option>
        ))}
      </optgroup>
    );
  }
}

原始测试:

 it('should change option', () => {
    render(
      <SelectFile
        fileList={fileList}
        handleChange={handleChange}
        selected=""
      />,
    );

    const selectElement = screen.getByDisplayValue('Select an option');
    const allOptions = screen.getAllByRole('option');

    const optionSelected = fileList.adonis[1];

    expect(selectElement).toHaveValue('');

    act(() => {
      userEvent.selectOptions(selectElement, optionSelected);
    });

    expect(handleChange).toHaveBeenCalledTimes(1);
    expect(selectElement).toHaveValue(optionSelected); // returns "" (default value)
    expect((allOptions[0] as HTMLOptionElement).selected).toBe(false);
    expect((allOptions[1] as HTMLOptionElement).selected).toBe(true);
    expect((allOptions[2] as HTMLOptionElement).selected).toBe(false);
    expect((allOptions[3] as HTMLOptionElement).selected).toBe(false);
    expect((allOptions[4] as HTMLOptionElement).selected).toBe(false);
  });

以及使用渲染的 html 修改后的测试:

 it('should change option', () => {
    render(
      <div>
        <div className="flex mr-10">
          <h3 className="text-lg font-bold mr-4">Select a file</h3>
          <select className="bg-slate-600 rounded w-auto">
            <option value="">Select an option</option>
            <optgroup label="ADONIS">
              <option value="adonis-03-02-2022.json">
                adonis-03-02-2022.json
              </option>
              <option value="adonis-02-02-2022.json">
                adonis-02-02-2022.json
              </option>
            </optgroup>
            <optgroup label="ERRORS">
              <option value="http_errors-03-03-2022.log">
                http_errors-03-03-2022.log
              </option>
              <option value="http_errors-04-02-2022.log">
                http_errors-04-02-2022.log
              </option>
            </optgroup>
          </select>
        </div>
      </div>,
    );

    const selectElement = screen.getByDisplayValue('Select an option');
    const allOptions = screen.getAllByRole('option');

    const optionSelected = fileList.adonis[1];

    expect(selectElement).toHaveValue('');

    act(() => {
      userEvent.selectOptions(selectElement, optionSelected);
    });

    expect(selectElement).toHaveValue(optionSelected); // this returns the optionSelected value
    expect((allOptions[0] as HTMLOptionElement).selected).toBe(false);
    expect((allOptions[1] as HTMLOptionElement).selected).toBe(true);
    expect((allOptions[2] as HTMLOptionElement).selected).toBe(false);
    expect((allOptions[3] as HTMLOptionElement).selected).toBe(false);
    expect((allOptions[4] as HTMLOptionElement).selected).toBe(false);
  });

考虑到它适用于修改后的测试,我无法解释为什么它不适用于原始测试。 我认为这是由于 optgroup 造成的,但似乎并非如此,所以现在我不知道为什么。


编辑:测试的最终版本:

  it('should change option', () => {
    const mockHandleChange = handleChange.mockImplementation(
      (cb) => (e) => cb(e.target.value),
    );

    render(
      <SelectWrapper fileList={fileList} handleChange={mockHandleChange} />,
    );

    const selectElement = screen.getByDisplayValue('Select an option');

    const optionSelected = fileList.adonis[1];

    expect(selectElement).toHaveValue('');

    act(() => {
      userEvent.selectOptions(selectElement, optionSelected);
    });

    expect(handleChange).toHaveBeenCalledTimes(2); // 1 for cb wrapper, 1 for select
    expect(selectElement).toHaveValue(optionSelected);
  });
});

const SelectWrapper = ({ handleChange, fileList }) => {
  const [selected, setSelected] = useState('');

  const mockHandleChange = handleChange(setSelected);

  return (
    <SelectFile
      fileList={fileList}
      handleChange={mockHandleChange}
      selected={selected}
    />
  );
};

我创建了一个包装器,使其像您在另一个组件中使用的那样,包装了模拟 function,现在它更改了值,您可以访问模拟。

由于在您的测试中您只渲染Select (这是一个受控组件:它从其父级接收当前值和onChange回调),具有固定的selected道具,您不能期望在触发更改时更改选定的选项select 上的事件。您只能期望已调用onChange回调(就像您所做的那样)。

对于这种组件,您需要测试selected的 props 是否被尊重(选择的选项是好的),并且当用户选择一个新选项时调用提供的回调(您已经完成了这部分)。

您需要添加一个带有现有选项的测试作为selected的道具(不是空字符串),然后检查selected的选项是否正确。 我建议您使用https://github.com/testing-library/jest-dom#tohavevalue来自https://github.com/testing-library/jest-dom

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM