Jest · Unit tests that survive refactors
How do you test DOM behaviour in Jest?
Short answer
Render markup into the jsdom document, query it by role or label, and interact through realistic events. Assert on visible DOM state rather than internal variables.
A DOM test should look like a user interaction. The arrangement is the rendered markup, the action is a click, typing or submit, and the assertion is the resulting visible state. Querying by role and accessible name keeps the test resilient to markup refactors.
import { screen } from "@testing-library/dom";
import userEvent from "@testing-library/user-event";
test("toggles the panel", async () => {
const user = userEvent.setup();
document.body.innerHTML =
'<button aria-expanded="false">Details</button><div hidden>More</div>';
const button = screen.getByRole("button", { name: "Details" });
await user.click(button);
expect(button).toHaveAttribute("aria-expanded", "true");
});Query and interaction rules
- Prefer
getByRole,getByLabelTextandgetByTextover CSS selectors - Use
userEventfor clicks and typing because it models real event sequences - Use
findByandwaitForfor updates that happen after an event loop turn - Assert on attributes, text and visibility that a user can perceive
- Avoid reading component state or calling internal methods
Use getBy when the element must already exist, queryBy to assert absence, and findBy when it appears asynchronously. The distinction matters because getBy throws on a missing element, which produces a clearer failure than a null check.
Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1When do you use queryBy instead of getBy?
- 2Why is userEvent preferred over fireEvent?
- 3How do you assert that an element is not present?
- 4Why should DOM tests avoid CSS selectors?
Say it in the interview
I render the markup, query it by role, label or text, and interact with userEvent. I assert on what the user can see, such as text, attributes and visibility. getBy is for elements that must exist, queryBy for absence, and findBy for async appearance. I avoid reaching into internal state, because that couples the test to the implementation.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Jest mock interview and get scored feedback on depth, clarity and confidence.