How do I trigger a change event on radio buttons in react-testing-library?

if you have a label like with Radio Component of Material-ui, you could use:

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

or you can add https://github.com/testing-library/jest-dom matchers and check it in this way:

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

As of May 2020 using React 16.13 and react-testing-library 10.0, the accepted answer does not work (the test itself passes but doesn't actually do anything meaningful).

I can't find any reference to radio buttons in the documentation for react-testing-library or even React itself. However, here's an example (using Typescript) that works properly as far as I can tell.

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 handlers will work in the browser but not with react-testing-library because they don't fire when you call fireEvent.change()

The dummy onChange handlers are required to avoid a React warning: "If the field should be mutable use defaultChecked". You can't use defaultChecked though, because it stops you setting the state in code (i.e. clicking the button at the bottom doesn't update the radio)

So on the whole, it looks like React wants you to use onChange but react-testing-library only works with onClick, so this is a bit of a fudge.


Update

As people pointed out my original solution was wrong.

Nowadays I suggest you use userEvent for better-simulating user interactions.

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"));
});

First, you don't have to call rerender. You use rerender only when you want the component to receive different props. See link.

Whenever you call fireEvent the component will render like it would in your normal app.

It's correct to fire a change event, but you must pass a second parameter with the event data.

This example works:

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')
});