Home > Software engineering >  How to test checkbox checked with React Testing Library?
How to test checkbox checked with React Testing Library?

Time:08-01

are there anyone see the problem here? If I put checked in state on change it works. But I don't want to duplicate the checked state data and watch it every single props change.

Parent components gets the latest checked information and put it in own state and if parent isChecked change, FormCheckBox isChecked changes. I think, it works async and when I reach latest line of my test code, parent update does not finish, so I see the stale isChecked.

    export default function FormCheckBox(props: IFormCheckBoxProps) {
      const { onChange, label, isChecked, error, errorModelLabel = '' } = props
    
      const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        onChange(event.currentTarget.checked)
      }
    
      const errorMsg = getFormErrorMsg(error, errorModelLabel)
    
      return (
        <FormGroup
          as={Row}
          className={cx('mb-3', { 'is-invalid': errorMsg })}
          controlId="catalogDescription"
          data-testid="slvy-formcheckbox"
        >
          <FormLabel column className="d-flex align-items-center justify-content-end fw-bold" sm={2}>
            {label}
          </FormLabel>
          <Col className="d-flex align-items-center" sm={10}>
            <input
              checked={isChecked}
              className={cx('checkbox', { 'is-invalid': errorMsg })}
              type="checkbox"
              onChange={handleChange}
            />
            <Form.Control.Feedback type="invalid">{errorMsg}</Form.Control.Feedback>
          </Col>
        </FormGroup>
      )
    }


 it('checkbox must use fireEvent.click', async () => {
    const handleChange = jest.fn()
    props.isChecked = false
    props.onChange = handleChange

    const { container } = render(<FormCheckBox {...props} />)
    const checkbox = container.querySelectorAll("input[type='checkbox']")[0] as HTMLInputElement

    fireEvent.click(checkbox)
    expect(handleChange).toHaveBeenCalledTimes(1)
    
    expect(checkbox.checked).toBe(true)
  })

CodePudding user response:

Since the FormCheckBox is a controlled component, the input’s value is always driven by the React state. So you need a wrapper component to provide this state. In your test case, even though the mock handleChange function is called, we need to update the isChecked state so that the FormCheckBox component will re-render with this new state.

E.g.

index.tsx:

import React from 'react';

export default function FormCheckBox({ onChange, isChecked }) {

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    onChange(event.currentTarget.checked);
  };

  return <input checked={isChecked} type="checkbox" onChange={handleChange} />;
}

To keep things simple. I removed the irrelevant code.

index.test.tsx:

import { fireEvent, render } from '@testing-library/react';
import React, { useState } from 'react';
import FormCheckBox from './';

describe('73184212', () => {
  test('should pass', () => {
    const Wrap = () => {
      const [isChecked, setIsChecked] = useState(false);
      return <FormCheckBox isChecked={isChecked} onChange={() => setIsChecked(!isChecked)} />;
    };
    const { container } = render(<Wrap />);
    const checkbox = container.querySelectorAll("input[type='checkbox']")[0] as HTMLInputElement;
    fireEvent.click(checkbox);
    expect(checkbox.checked).toBe(true);
  });
});

Test result:

 PASS  stackoverflow/73184212/index.test.tsx (10.998 s)
  73184212
    ✓ should pass (30 ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 index.tsx |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        11.607 s
  • Related