【问题标题】:How do I trigger a change event on radio buttons in react-testing-library?如何在 react-testing-library 中的单选按钮上触发更改事件?
【发布时间】:2019-07-09 02:50:50
【问题描述】:

我正在迁移到 react-testing-library,但不知道如何触发此事件并获得更改的结果。

我尝试使用fireEvent 函数来触发更改,然后尝试了rerender 函数,但我似乎无法让它工作。

App.js

import React, { useState } from "react";
import logo from "./logo.svg";
import "./App.css";

const options = {
  DoTheThing: 'DoTheThing',
  DoOtherThing: 'DoOtherThing',
};

function App() {
  const [action, setAction] = useState(options.DoTheThing);

  return (
    <div className="App">
      <header className="App-header">
        <form>
          <fieldset>
            <label>
              <input
                type="radio"
                name="radio1"
                value={options.DoTheThing}
                checked={action === options.DoTheThing}
                onChange={event => setAction(event.target.value)}
              />
              First
            </label>

            <label>
              <input
                type="radio"
                name="radio1"
                value={options.DoOtherThing}
                checked={action === options.DoOtherThing}
                onChange={event => setAction(event.target.value)}
              />
              Second
            </label>
          </fieldset>
        </form>
      </header>
    </div>
  );
}

export default App;

App.test.js

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

afterEach(cleanup);

it('should change the value ', () => {
  const {getByLabelText, rerender } = render(<App/>);
  const second = getByLabelText(/Second/);

  fireEvent.change(second);
  rerender(<App/>);

  expect(document.forms[0].elements.radio1.value).toEqual("DoOtherThing");

});

【问题讨论】:

    标签: reactjs jestjs react-testing-library


    【解决方案1】:

    如果你有一个像 Material-ui 的 Radio Component 这样的标签,你可以使用:

    const labelRadio: HTMLInputElement = getByLabelText('Label of Radio');
    expect(labelRadio.checked).toEqual(false);
    fireEvent.click(labelRadio);
    expect(androidRadio.checked).toEqual(true);
    

    或者你可以添加https://github.com/testing-library/jest-dom匹配器,这样检查:

    expect(getByLabelText('Label of Radio')).not.toBeChecked();
    fireEvent.click(labelRadio);
    expect(getByLabelText('Label of Radio')).toBeChecked();
    

    【讨论】:

      【解决方案2】:

      首先,您不必致电rerender。仅当您希望组件接收不同的道具时才使用rerender。见link

      每当您调用 fireEvent 时,组件都会像在您的普通应用中一样呈现。

      触发change 事件是正确的,但您必须传递带有事件数据的第二个参数。

      这个例子有效:

      import React from "react";
      import { render, fireEvent } from "react-testing-library";
      
      test("radio", () => {
        const { getByLabelText } = render(
          <form>
            <label>
               First <input type="radio" name="radio1" value="first" />
            </label>
            <label>
              Second <input type="radio" name="radio1" value="second" />
            </label>
          </form>
        );
      
        const radio = getByLabelText('First')
        fireEvent.change(radio, { target: { value: "second" } });
        expect(radio.value).toBe('second')
      });
      

      【讨论】:

      • 我最初发送了一个模拟事件,但后来看到一些 [大概] 较旧的信息表明这是不必要的。我只添加了重新渲染,因为我看不到更改本身。
      • 我记得我为什么要重新渲染。最终,我要测试的是值更改后一组特定输入的外观。我的第一步是能够断言收音机发生了变化。我正在使用waitForElement,但它只是超时并抛出。我应该为此创建一个新问题吗?
      • waitForElement 在发生异步事件时很有用,例如您正在等待fetch 回复。在这种情况下,您不需要等待任何事情,因为更新会立即发生。当waitForElement 失败时,我尝试改用wait。如果即使这样也不起作用,问题通常出在其他地方。通常,我的组件根本不会更新。
      • 这不是单选按钮的工作方式。它们的价值是固定的。只有他们的选中状态会改变。
      • 这段代码所做的只是将第一个单选按钮的值更改为“第二个”。现在您只有 2 个值为“second”的单选按钮,但它们都没有被选中。此外,它不会导致响应 onChange 处理程序被调用,所以它不是真的有用
      【解决方案3】:

      截至 2020 年 5 月,使用 React 16.13 和 react-testing-library 10.0,接受的答案不起作用(测试本身通过但实际上并没有做任何有意义的事情)。

      我在 react-testing-library 甚至 React 本身的文档中找不到任何对单选按钮的引用。但是,据我所知,这是一个可以正常工作的示例(使用 Typescript)。

      import React from "react";
      class State {
          radioValue: string = "one"
      }
      export default class RadioTest extends React.Component<{}, State>
      {
          state: State = new State();
      
          radioClick = (event: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
              this.setState({ radioValue: event.currentTarget.value });
          }
      
          render() {
              return (<>
                  <label>
                      One
                      <input type="radio" name="radio" onClick={this.radioClick}
                          value="one" onChange={() => {}}
                          checked={this.state.radioValue === "one"} />
                  </label>
                  <label>
                      Two
                      <input type="radio" name="radio" onClick={this.radioClick}
                          value="two" onChange={() => {}}
                          checked={this.state.radioValue === "two"} />
                  </label>
                  <div>current value={this.state.radioValue}</div>
                  <button onClick={() => this.setState({radioValue:"one"})}>Click</button>
              </>);
          }
      }
      
      test("radiotest", () => {
          const { getByLabelText, queryByText, getByText } = render(<RadioTest />);
          const one = getByLabelText('One') as HTMLInputElement
          const two = getByLabelText('Two') as HTMLInputElement
          expect(one).toBeChecked();
          expect(two).not.toBeChecked();
          expect(queryByText("current value=one")).toBeTruthy();
          fireEvent.click(two);
          expect(one).not.toBeChecked();
          expect(two).toBeChecked();
          expect(queryByText("current value=two")).toBeTruthy();
          fireEvent.click(getByText("Click"))
          expect(one).toBeChecked();
          expect(two).not.toBeChecked();
          expect(queryByText("current value=one")).toBeTruthy();
      });
      

      React onChange 处理程序将在浏览器中工作,但不适用于 react-testing-library,因为当您调用 fireEvent.change() 时它们不会触发

      需要虚拟 onChange 处理程序来避免 React 警告:“如果字段应该是可变的,请使用 defaultChecked”。但是你不能使用 defaultChecked,因为它会阻止你在代码中设置状态(即点击底部的按钮不会更新收音机)

      所以总的来说,看起来 React 希望你使用 onChange,但 react-testing-library 只适用于 onClick,所以这有点做作。

      【讨论】:

      • 这是我们问题的解决方案。 .change 不会触发行为
      【解决方案4】:

      补充@andy 的回答,这应该有效地测试两个收音机:

        it('should render successfully', async () => {
          render(
            <YourRadioGroup />
          );
      
          expect(screen.getByText('option 1')).toBeInTheDocument();
          expect(screen.getByText('option 2')).toBeInTheDocument();
        });
      
        it('should change checked option', () => {
          render(
            <YourRadioGroup />
          );
      
          const secondRadio = screen.getByLabelText('option 2');
          fireEvent.click(secondRadio);
          expect(secondRadio).toBeChecked();
      
          const firstRadio = screen.getByLabelText('option 1');
          fireEvent.click(firstRadio);
          expect(firstRadio).toBeChecked();
          expect(secondRadio).not.toBeChecked();
        });
      

      【讨论】:

        【解决方案5】:

        请从 react-testing-library 文档中尝试这个,“渲染”应该可以正常工作。同意@Gpx

        fireEvent.change(input, { target: { value: 'your_value_goes_here' } })
        expect(input.value).toBe('expected_value')
        

        【讨论】:

          【解决方案6】:

          我也有这项工作:

          test('Does stuff', async () => {
          // ... test prep ...
          
          const formEl = screen.getByTestId('form_test_id')
          
          // You can use screen.getByLabelText here instead of DOM queries 
          // if you've got a nicely laid out form
          const defaultInput = formEl.querySelector('input[value="default_value"]')
          const newValueInput = formEl.querySelector('input[value="new_value"]')
          
          // Confirm your baseline
          expect(defaultInput.checked).toEqual(true)
          expect(newValueInput.checked).toEqual(false)
          
          // Fire the change event
          await act(async () => {
          fireEvent.change(newValueInput, { target: { checked: true } }) 
          // To trigger any onChange listeners
          fireEvent.blur(newValueInput)
          })
          
          // Confirm expected form state(s)
          expect(defaultInput.checked).toEqual(false)
          expect(newValueInput.checked).toEqual(true)
          
          })
          

          【讨论】:

            猜你喜欢
            • 2019-07-17
            • 1970-01-01
            • 1970-01-01
            • 2015-09-02
            • 1970-01-01
            • 2012-07-14
            • 1970-01-01
            • 1970-01-01
            • 2019-07-28
            相关资源
            最近更新 更多