如何在 react-testing-library 中的单选按钮上触发更改事件?
How do I trigger a change event on radio buttons in react-testing-library?
我正在转移到 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");
});
更新
正如人们指出我原来的解决方案是错误的。
现在我建议您使用 userEvent
来更好地模拟用户交互。
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("radio", () => {
const user = userEvent.setup();
render(
<form>
<label>
First <input type="radio" name="radio1" value="first" />
</label>
<label>
Second <input type="radio" name="radio1" value="second" />
</label>
</form>
)
await user.click(screen.getByLabelText("Second"));
});
首先,您不必调用 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')
});
请从 react-testing-library 文档中尝试,"render" 应该可以正常工作。同意@Gpx
fireEvent.change(input, { target: { value: 'your_value_goes_here' } })
expect(input.value).toBe('expected_value')
我也有这个工作:
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)
})
截至 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
,所以这有点胡扯。
如果你有一个像 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();
补充@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();
});
这对我有用(使用单选按钮,而不是单选组):
// some code here to make sure the screen has finished rendering, and I have all radio buttons in the DOM (I am expecting 5 containers):
await waitFor(() => expect(screen.getAllByTestId('some-slow-loading-container')).toHaveLength(5))
// get all "true" labeled radio buttons by test id (or by role + name or whatever):
const allTrueLabelRadioButtons = screen.getAllByTestId('true-label-radio-button');
// loop over the array of HTML elements found:
for (const trueLabelRadioButton of allTrueLabelRadioButtons) {
// using fireEvent instead of userEvent because of some bugs with the components library I am stuck with. Usually I use userEvent:
fireEvent.click(trueLabelRadioButton)
}
// check whatever you are expecting to happen after all radio buttons are set to "true".
//...
我正在转移到 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");
});
更新
正如人们指出我原来的解决方案是错误的。
现在我建议您使用 userEvent
来更好地模拟用户交互。
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("radio", () => {
const user = userEvent.setup();
render(
<form>
<label>
First <input type="radio" name="radio1" value="first" />
</label>
<label>
Second <input type="radio" name="radio1" value="second" />
</label>
</form>
)
await user.click(screen.getByLabelText("Second"));
});
首先,您不必调用 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')
});
请从 react-testing-library 文档中尝试,"render" 应该可以正常工作。同意@Gpx
fireEvent.change(input, { target: { value: 'your_value_goes_here' } })
expect(input.value).toBe('expected_value')
我也有这个工作:
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)
})
截至 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
,所以这有点胡扯。
如果你有一个像 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();
补充@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();
});
这对我有用(使用单选按钮,而不是单选组):
// some code here to make sure the screen has finished rendering, and I have all radio buttons in the DOM (I am expecting 5 containers):
await waitFor(() => expect(screen.getAllByTestId('some-slow-loading-container')).toHaveLength(5))
// get all "true" labeled radio buttons by test id (or by role + name or whatever):
const allTrueLabelRadioButtons = screen.getAllByTestId('true-label-radio-button');
// loop over the array of HTML elements found:
for (const trueLabelRadioButton of allTrueLabelRadioButtons) {
// using fireEvent instead of userEvent because of some bugs with the components library I am stuck with. Usually I use userEvent:
fireEvent.click(trueLabelRadioButton)
}
// check whatever you are expecting to happen after all radio buttons are set to "true".
//...