Как вызвать событие изменения на переключателях в реагирующей тестирующей библиотеке?
Я нахожусь в процессе перехода к библиотеке реагирования-тестирования и не знаю, как вызвать это событие и получить результаты изменений.
Я пытался использовать 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");
});
8 ответов
Во-первых, вам не нужно звонить rerender
, Ты используешь rerender
только когда вы хотите, чтобы компонент получил разные реквизиты. Смотрите ссылку.
Всякий раз, когда вы звоните 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 = getByLabelTest('First')
fireEvent.change(radio, { target: { value: "second" } });
expect(radio.value).toBe('second')
});
Если у вас есть метка, такая как Radio Component of Material-ui, вы можете использовать:
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();
По состоянию на май 2020 года с использованием React 16.13 и react-testing-library 10.0 принятый ответ не работает (сам тест проходит, но на самом деле ничего значимого не делает).
Я не могу найти никаких ссылок на переключатели в документации для библиотеки react-testing или даже 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 будут работать в браузере, но не с библиотекой response-testing-library, потому что они не срабатывают при вызове fireEvent.change()
Фиктивные обработчики onChange необходимы, чтобы избежать предупреждения React: "Если поле должно быть изменяемым, используйтеdefaultChecked
". Вы не можете использовать defaultChecked, потому что он не позволяет вам устанавливать состояние в коде (т.е. нажатие кнопки внизу не обновляет радио)
В общем, похоже, что React хочет, чтобы вы использовали onChange
но библиотека реагирования-тестирования работает только с onClick
, так что это немного выдумка.
Дополняя ответ @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();
});
Пожалуйста, попробуйте это из документации по response-testing-library, "render" должен работать нормально. Согласен с @Gpx
fireEvent.change(input, { target: { value: 'your_value_goes_here' } })
expect(input.value).toBe('expected_value')
Можно использовать указанное ниже решение для RadioGroup. Это будет работать для RTL.
render(component);
const fieldset = screen.getByLabelText(/other applicable cards/i);
expect(fieldset.checked).toEqual(false);
fireEvent.click(fieldset, { target: { value: '2' } });
expect(fieldset.value).toEqual('2');
Можно использовать для управления несколькими событиями щелчка по радио.
render(component);
const fieldset = screen.getByLabelText(/other applicable cards/i);
expect(fieldset.checked).toEqual(false);
fireEvent.click(fieldset, { target: { value: '2' } });
expect(fieldset.value).toEqual('2');
const fieldset1 = screen.getByLabelText(/original amex platinum/i);
expect(fieldset1.checked).toEqual(false);
fireEvent.click(fieldset1, { target: { value: '1' } });
expect(fieldset1.value).toEqual('1');
Это сработало для меня (работа с переключателями, а не с группами переключателей):
// 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".
//...
У меня тоже была эта работа:
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)
})