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
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: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 theh1
element.Since version 14 the
user-event
APIs return promises.You need to
await
those.I recommend
@typescript-eslint/no-floating-promises
Using an instance from
userEvent.setup()
instead of the direct APIs is recommended.See https://testing-library.com/docs/user-event/intro#writing-tests-with-userevent