如何使用react-testing-library测试react-select

时间:2019-04-08 14:33:44

标签: reactjs integration-testing react-select react-testing-library

App.js

import React, { Component } from "react";
import Select from "react-select";

const SELECT_OPTIONS = ["FOO", "BAR"].map(e => {
  return { value: e, label: e };
});

class App extends Component {
  state = {
    selected: SELECT_OPTIONS[0].value
  };

  handleSelectChange = e => {
    this.setState({ selected: e.value });
  };

  render() {
    const { selected } = this.state;
    const value = { value: selected, label: selected };
    return (
      <div className="App">
        <div data-testid="select">
          <Select
            multi={false}
            value={value}
            options={SELECT_OPTIONS}
            onChange={this.handleSelectChange}
          />
        </div>
        <p data-testid="select-output">{selected}</p>
      </div>
    );
  }
}

export default App;

App.test.js

import React from "react";
import {
  render,
  fireEvent,
  cleanup,
  waitForElement,
  getByText
} from "react-testing-library";
import App from "./App";

afterEach(cleanup);

const setup = () => {
  const utils = render(<App />);
  const selectOutput = utils.getByTestId("select-output");
  const selectInput = document.getElementById("react-select-2-input");
  return { selectOutput, selectInput };
};

test("it can change selected item", async () => {
  const { selectOutput, selectInput } = setup();
  getByText(selectOutput, "FOO");
  fireEvent.change(selectInput, { target: { value: "BAR" } });
  await waitForElement(() => getByText(selectOutput, "BAR"));
});

这个最小的示例在浏览器中可以正常工作,但是测试失败。我认为没有调用onChange处理程序。如何在测试中触发onChange回调?查找fireEvent元素的首选方法是什么?谢谢

8 个答案:

答案 0 :(得分:9)

类似于@momimomo的答案,我写了一个小助手来从TypeScript中的react-select中选择一个选项。

帮助文件:

import { getByText, findByText, fireEvent } from '@testing-library/react';

const keyDownEvent = {
    key: 'ArrowDown',
};

export async function selectOption(container: HTMLElement, optionText: string) {
    const placeholder = getByText(container, 'Select...');
    fireEvent.keyDown(placeholder, keyDownEvent);
    await findByText(container, optionText);
    fireEvent.click(getByText(container, optionText));
}

用法:

export const MyComponent: React.FunctionComponent = () => {
    return (
        <div data-testid="day-selector">
            <Select {...reactSelectOptions} />
        </div>
    );
};
it('can select an option', async () => {
    const { getByTestId } = render(<MyComponent />);
    // Open the react-select options then click on "Monday".
    await selectOption(getByTestId('day-selector'), 'Monday');
});

答案 1 :(得分:3)

最后,有一个图书馆可以帮助我们:https://testing-library.com/docs/ecosystem-react-select-event。对于单选或单选都非常适用:

来自@testing-library/react文档:

import React from 'react'
import Select from 'react-select'
import { render } from '@testing-library/react'
import selectEvent from 'react-select-event'

const { getByTestId, getByLabelText } = render(
  <form data-testid="form">
    <label htmlFor="food">Food</label>
    <Select options={OPTIONS} name="food" inputId="food" isMulti />
  </form>
)
expect(getByTestId('form')).toHaveFormValues({ food: '' }) // empty select

// select two values...
await selectEvent.select(getByLabelText('Food'), ['Strawberry', 'Mango'])
expect(getByTestId('form')).toHaveFormValues({ food: ['strawberry', 'mango'] })

// ...and add a third one
await selectEvent.select(getByLabelText('Food'), 'Chocolate')
expect(getByTestId('form')).toHaveFormValues({
  food: ['strawberry', 'mango', 'chocolate'],
})

感谢https://github.com/romgain/react-select-event如此出色的软件包!

答案 2 :(得分:2)

此解决方案对我有用。

fireEvent.change(getByTestId("select-test-id"), { target: { value: "1" } });

希望它可以帮助奋斗者。

答案 3 :(得分:0)

这一定是关于RTL:D的最常见的问题

最好的策略是使用jest.mock(或测试框架中的等效项)来模拟选择并呈现HTML选择。

有关为什么这是最佳方法的更多信息,我也写了一些适用于这种情况的内容。 OP询问了Material-UI中的选择,但是想法是一样的。

Original question和我的答案:

  

因为您无法控制该UI。它是在第3方模块中定义的。

     

因此,您有两个选择:

     

您可以找出材质库创建的HTML,然后使用container.querySelector查找其元素并与其进行交互。需要一段时间,但应该可以。完成所有这些操作后,您必须希望在每个新发行版中,它们不要过多地更改DOM结构,否则您可能必须更新所有测试。

     

另一种选择是相信Material-UI将使组件能够正常工作并且用户可以使用。基于这种信任,您只需在测试中替换该组件即可。

     

是的,选项一测试用户看到的内容,但是选项二更易于维护。

     

根据我的经验,第二个选项很好,但是当然,您的用例可能有所不同,您可能必须测试实际的组件。

这是一个如何模拟选择的示例:

jest.mock("react-select", () => ({ options, value, onChange }) => {
  function handleChange(event) {
    const option = options.find(
      option => option.value === event.currentTarget.value
    );
    onChange(option);
  }
  return (
    <select data-testid="select" value={value} onChange={handleChange}>
      {options.map(({ label, value }) => (
        <option key={value} value={value}>
          {label}
        </option>
      ))}
    </select>
  );
});

您可以阅读更多here

答案 4 :(得分:0)

在我的项目中,我正在使用react-testing-library和jest-dom。 我遇到了同样的问题-经过一番调查,我发现了基于线程https://github.com/airbnb/enzyme/issues/400

的解决方案

请注意,渲染的顶层功能必须是异步的,并且必须分步执行。

在这种情况下,无需使用焦点事件,它将允许选择多个值。

此外,getSelectItem中必须有一个异步回调。

SmartForm

答案 5 :(得分:0)

export async function selectOption(container: HTMLElement, optionText: string) {
  let listControl: any = '';
  await waitForElement(
    () => (listControl = container.querySelector('.Select-control')),
  );
  fireEvent.mouseDown(listControl);
  await wait();
  const option = getByText(container, optionText);
  fireEvent.mouseDown(option);
  await wait();
}

注意: container:用于选择框的容器(例如:container = getByTestId('seclectTestId'))

答案 6 :(得分:0)

一种适用于我的用例的解决方案,不需要在@Steve Vaughan上找到任何反应选择模拟或单独的库(由于react-testing-library spectrum chat.

不利之处是我们必须使用container.querySelector,RTL建议使用with temp as ( SELECT a.*, b.* FROM TABLEA a JOIN TABLEB b on a.answer = b.interest ) SELECT t.Name, t.Question, t.Answer from temp UNION ALL SELECT t.Name, 'Segment' as Question, t.Segment as Answer from temp 来支持更灵活的选择器。

答案 7 :(得分:0)

如果您不使用 label 元素,使用 react-select-event 的方法是:

const select = screen.container.querySelector(
  "input[name='select']"
);

selectEvent.select(select, "Value");