React state not being updated in Jest while working in the application

When I click the button it increments the value by the amount written in the input, it works when I do that in the app but when I try to use Jest and first fireEvent.change() the value of an input to 10 and then fireEvent.click() on the button it doesnt increment it and the value stays at 0.

Here is the error that Im getting:

expect(received).toBe(expected) // Object.is equality

   Expected: 10
   Received: 0

     33 |     fireEvent.click(btnPlus);
     34 |
   > 35 |     expect(parseInt(val.textContent)).toBe(10);
        |                                       ^
     36 |   });
     37 | });
     38 |

Here is the test file:

import React from 'react';
import Counter from './Counter';
import { render, fireEvent } from '@testing-library/react';
describe('Counter works', () => {
  let comp;
  let inp;
  let btnPlus;
  let val;
  beforeAll(() => {
    comp = render(<Counter />);
    inp = comp.getByTestId('inp');
    btnPlus = comp.getByTestId('btn-+');
    val = comp.getByTestId('counter-value');
  });

  it('Counter exists', () => {
    expect(comp).toBeTruthy();
  });
  it('Input works', () => {
    expect(inp.value).toBe('');

    fireEvent.change(inp, {
      target: {
        value: 10,
      },
    });

    expect(parseInt(inp.value)).toBe(10);

    fireEvent.click(btnPlus);

    expect(parseInt(val.textContent)).toBe(10);
  });
});

The general Counter file:


const Counter = () => {
  const [state, setState] = useState({
    count: 0,
    inpText: '',
  });
  const setNum = (num) => {
    setState((prev) => {
      return {
        ...prev,
        count: prev.count + num,
      };
    });
  };
  const setInp = (e) => {
    setState((prev) => {
      return {
        ...prev,
        inpText: e?.target.value,
      };
    });
  };
  return (
    <>
      <h1
        data-testid='counter-value'
        style={{
          color: 'white',
          position: 'absolute',
          top: '45%',
          left: '50%',
          transform: 'translate(-50%,-60%)',
        }}>
        {state.count}
      </h1>
      <div id='flex'>
        <Button setNum={setNum} plus='+' num={parseInt(state.inpText)} />
        <input data-testid='inp' value={state.inpText} onChange={setInp} />
        <Button setNum={setNum} plus='-' num={-parseInt(state.inpText)} />
      </div>
    </>
  );
};



And the Button:


const Button = (props) => {
  return (
    <button
      data-testid={`btn-${props.plus}`}
      onClick={() => {
        props.setNum(props.num);
      }}>
      {props.plus}
    </button>
  );
};


Solution 1:

I would suggest avoiding initial component setup in beforeAll/beforeEach functions. Each test case should run in isolation and not be affected by operations executed in other tests.

Instead, create a helper function with that logic and call it on every test.

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

describe('Counter works', () => {
    let comp;
    let inp;
    let btnPlus;
    let val;
  
    const renderComponent = () => {
        comp = render(<Counter />);
        inp = comp.getByTestId('inp');
        btnPlus = comp.getByTestId('btn-+');
        val = comp.getByTestId('counter-value');
    }

    it('Counter exists', () => {
        renderComponent();
        expect(comp).toBeTruthy();
    });
  
    it('Input works', () => {
        renderComponent();
        expect(inp.value).toBe('');
        fireEvent.change(inp, { target: { value: 10 } });
        expect(inp.value).toBe('10');
        fireEvent.click(btnPlus);
        expect(val.textContent).toBe('10');
    });
});