skip to Main Content

When I click to increase button the value of the count is increasing so text in the h1 is also changing, but when I want to test it, its failing. When I change the expected value to 0 on the expect part, the test doesn’t fail. What is the reason behind it? Appreciate your help.

Component that I want to test

import { useState } from "react"

function Counter() {
    const [count, setCount] = useState(0)

  return (
    <div>
        <h1>{count}</h1>
        <button onClick={() => setCount(count + 1)}>Increase</button>
        <button onClick={() => setCount(count - 1)}>Decrease</button>
    </div>
  )
}
export default Counter

My test code but I can’t get my expected output from it.

import {screen, render} from "@testing-library/react"
import userEvent from "@testing-library/user-event"

import Counter from "."

test("counter component test", () => {
    render(<Counter />)

    const count = screen.getByText("0")

    const increaseBtn = screen.getByText("Increase")

    userEvent.click(increaseBtn);
    expect(count).toHaveTextContent("1")
})

3

Answers


  1. Testing components where you initiate some state change needs some "wait" time for it to change.

    In your case you should use waitFor form the @testing-library/react and then assert in the callback:

    import {screen, render, waitFor} from "@testing-library/react"
    import userEvent from "@testing-library/user-event"
    
    import Counter from "."
    
    test("counter component test", () => {
        render(<Counter />)
    
        const count = screen.getByText("0")
    
        const increaseBtn = screen.getByText("Increase")
    
        userEvent.click(increaseBtn);
    
        waitFor(() => {
          expect(count).toHaveTextContent("1");
       })
    })
    
    Login or Signup to reply.
  2. My understanding is that you already solved your issue, just want to explain why you needed the waitFor().

    When you start your test you fetch for the h1 element, and get it. And verify that the text inside it is "0".
    At the end you test it again for the text to be "1". It fails because the text isn’t "1". Why is it not "1"?

    When you setCount() you told React to update the state, but React takes a few milliseconds to do exactly that.
    So when you tried to check if the text was updated right after clicking, the text in fact was not updated, because state still wasn’t updated.

    Using waitFor(() => {expect(count).toHaveTextContent("1")}) solves your problem because you wait the extra milliseconds React took to update your state, and therefore updating the h1 element.

    Login or Signup to reply.
  3. Since version 14 the user-event APIs return promises.
    You need to await those.

    test('increase the count', async () => {
      // ...
      await userEvent.click(element)
      // ...
    

    I recommend @typescript-eslint/no-floating-promises


    Using an instance from userEvent.setup() instead of the direct APIs is recommended.

    test('increase the count', async () => {
      const user = userEvent.setup()
      // ...
      await user.click(element)
      // ...
    

    See https://testing-library.com/docs/user-event/intro#writing-tests-with-userevent

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search