Jest/react-testing-library: Clicking a button didn't update the value in test

Viewed 3486

Initial Question

I have a simple increment/decrement component.

It shows a number count and 2 buttons that either increment or decrements by 1.

There is also an input; if a numeric value is supplied in the input then the buttons use the input value instead of 1 to inc/decrement.

I have this test:

it(`should increment by input value, if value > 0`, () => {
  const { input } = setup();

  // Change input value to 4
  fireEvent.change(input, {target: { value: 4}});

  // Find and click +n button
  const btn = screen.getByText(`+n`);
  fireEvent.click(btn);

  // Find and check updated count
  const updatedCount = parseInt(screen.getByTestId(`count`).textContent, 10)
  expect(updatedCount).toBe(4);
})

The Issue: The updated count here is returning 1 (default in useState), I expected 4.

expect(parseInt(input.value, 10)).toBe(4) passes and the input onChange is hooked up.

The Question: Why is the updated input value not being used?

Additional Info: Although I'm not sure, I thought maybe it wasn't updating the useState on change, so I also added a keyboard event to hit the enter key on the input, after I changed its value. I was hoping to further simulate a user and update the state but I had no luck with this.

Any help much appreciated! Apologies for anything that's out of place, I've only been looking at Jest/RTL the last couple, of days.



Reply Info

Test Component:

import React, { useState } from "react";

const MyTest = () => {
  const [count, setCount] = useState(0);
  const [value, setValue] = useState(1);

  const incCount = (n) => {
    if (n !== 0) {
      count + n <= 10 && setCount((currCount) => currCount + n);
      return;
    }

    if (count < 10) setCount((currCount) => currCount + 1);
  };
  const decCount = (n) => {
    if (n !== 0) {
      count - n >= 0 && setCount((currCount) => currCount - n);
      return;
    }

    if (count > 0) {
      setCount((currCount) => currCount - 1);
    }
  };

  return (
    <>
      <div>value: {value}</div>
      <div data-testid="count">{count}</div>

      <br />

      <label htmlFor="inp">N Input</label>
      <br />
      <input
        type="texts"
        placeholder="inc or dec by num"
        id="inp"
        value={value}
        onChange={(e) => setValue(e.target.value)}
      />
      <br />
      <button type="button" onClick={() => decCount(parseInt(value))}>
        -n
      </button>
      <button type="button" onClick={() => incCount(parseInt(value))}>
        +n
      </button>
    </>
  );
};

export default MyTest;

Test:

import React from 'react';
import {screen, fireEvent, render, cleanup, waitFor} from '@testing-library/react';
import "@testing-library/jest-dom/extend-expect";
import MyTest from './myTest';

describe(`Increment and decrement the count when relevant button is pressed`, () => {
  // Get component
  const setup = () => {
    const utils = render(<MyTest />)
    const initCount = parseInt(screen.getByTestId(`count`).textContent, 10)
    const input = screen.getByLabelText(`N Input`);
    return {
      initCount,
      input,
      ...utils
    }
  };

  // ...other tests here

  it(`should increment by input value, if value > 0`, () => {
    const { input } = setup();

    // Change input value to 4
    fireEvent.change(input, {target: { value: 4}});

    // Find and click +n button
    const btn = screen.getByText(`+n`);
    fireEvent.click(btn);

    // Find and check updated count
    const updatedCount = parseInt(screen.getByTestId(`count`).textContent, 10)
    expect(updatedCount).toBe(4);
  })
})
1 Answers

You have a typo in your type attribute. Change this

<input type="texts" {...} />

To this

<input type="text" {...} />

And the test will work again. My guess is that by specifying the <input> an invalid type attribute, the library doesn't know which role your input is, so it cannot handle the onchange event properly.

I'm not sure why updating to the new version of react-scripts and @testing-library/react will fix the problem even if you leave the typo there though.

Related