🧪 Simple Testing with ReactDOM: A Hands-On Guide for Beginners
“The more your tests resemble the way your software is used, the more confidence they can give you.” — Kent C. Dodds
Welcome to your first hands-on experience with testing React applications using ReactDOM
. In this guide, we’ll walk through how to write tests that not only verify your components work, but also simulate how users actually interact with them.
When we write tests, our goal is to mimic real-world usage. This gives us confidence that our application behaves correctly for:
But there’s a third, unwanted visitor in our test world: The Test User. This is the imaginary user that only exists in the test, performing things real users wouldn’t do. We want to avoid that.
🔗 Learn more about the “test user” concept here: Avoid the Test User
We’ll be testing a very simple Counter
component. You can interact with it at:
http://localhost:3000/counter
Here’s what we expect:
Current count: 0
We’ll manually render the component to a DOM node, just like a developer would, and simulate button clicks, just like a user would.
ReactDOM.createRoot
act
from react-dom/test-utils
to wrap interactions (required in React 18)import * as React from 'react'import { createRoot } from 'react-dom/client'import { act } from 'react-dom/test-utils'// Assuming this is your componentimport Counter from '../counter'test('counter increments and decrements', () => {const container = document.createElement('div')document.body.appendChild(container)act(() => {createRoot(container).render(<Counter />)})const incrementButton = container.querySelector('button.increment')const decrementButton = container.querySelector('button.decrement')const message = container.querySelector('div.message')expect(message.textContent).toBe('Current count: 0')act(() => {incrementButton.click()})expect(message.textContent).toBe('Current count: 1')act(() => {decrementButton.click()})expect(message.textContent).toBe('Current count: 0')document.body.removeChild(container)})
Instead of calling .click()
directly, try simulating an event more like the browser does — using dispatchEvent
.
This is especially useful when working with events that don’t have convenience methods (like mouseover
or keydown
).
const clickEvent = new MouseEvent('click', {bubbles: true,cancelable: true,button: 0,})act(() => {incrementButton.dispatchEvent(clickEvent)})
✅ Always set
bubbles: true
so the event can propagate properly.
ReactDOM
and simulate user interactionsdispatchEvent
for more flexible event simulationEvery time you write a test, ask yourself:
“Does this test reflect how the component is used in the real world?”
If the answer is yes, you’re on the right path. If not, you might be simulating a test user—and that leads to fragile, misleading tests.
Quick Links
Legal Stuff
Social Media