liveBook · Manning

Arrange act assert pattern

As explained in React in Depth

Arrange-Act-Assert is a test-organization pattern that separates setup, behavior execution, and result verification.

Arrange-Act-Assert organizes a test into setup, behavior execution, and result verification. The ACT phase may be omitted when no behavior occurs, and a test may contain multiple ACT–ASSERT rounds. section 10.2.1

Use the pattern to make component tests easier to read and maintain:

  • Arrange by rendering the component and preparing the interaction environment. section 10.2.1
  • Act by querying an accessible control and performing a user interaction. section 10.2.1
  • Assert by querying the resulting interface and checking the expected state. section 10.2.1
  • Extract repeated rendering, queries, user setup, and rerendering into a custom setup function when the test file grows. section 10.2.1

Testing a click counter

Arrange with render(<Counter />), create a userEvent instance, and locate the Increment button. Act with await user.click(increment). Assert that a heading named “Counter: 1” is present. For prop changes, arrange with render(<Counter start={10} />), act with rerender(<Counter start={20} />), and assert the retained displayed value. section 10.2.1

Listing 10.5 Clicking a button
import { render, screen } from "@testing-library/react";
import userEvent
  from "@testing-library/user-event";     #1
import "@testing-library/jest-dom";
import { Counter } from "./Counter";
test(     #2
  "Counter should start at the given value",    #2
  () => {...},   #2
);    #2
test(    #2
  "Counter should start at 0 if no value is given",   #2
  () => {...},    #2
);    #2
test(    #3
  "Counter should increment when button is clicked",   #3
  async () => {    #3
  // ARRANGE
  render(<Counter />);
  // ACT
  const user = userEvent.setup();    #4
  const increment = screen.getByRole(     #5
    "button",    #5
    { name: "Increment" },    #5
  );    #5
  await user.click(increment);     #6
  // ASSERT
  const heading = screen.getByRole(    #7
    "heading",    #7
    { name: "Counter: 1" },    #7
  );    #7
  expect(heading).toBeInTheDocument();
});

Shows the complete Arrange-Act-Assert flow for an asynchronous button interaction. section 10.2.1

Listing 10.7 The full test file with a convenient setup function
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import { Counter } from "./Counter";
function setup(start) {    #1
  const { rerender } = render(<Counter start={start} />);
  const getCounter = (val) => screen.getByRole(     #2
    "heading",    #2
    { name: `Counter: ${val}` },    #2
  );    #2
  const increment = screen.getByRole("button", { name: "Increment" });
  const decrement = screen.getByRole("button", { name: "Decrement" });
  const user = userEvent.setup();
  return {    #3
    getCounter,
    increment,
    decrement,
    user,
    rerender: (newStart) => rerender(<Counter start={newStart} />),
  };
}
test("Counter should start at the given value", () => {
  // ARRANGE
  const { getCounter } = setup(10);     #4
  // ASSERT
  expect(getCounter(10)).toBeInTheDocument();    #5
});
test("Counter should start at 0 if no value is given", () => {
  // ARRANGE
  const { getCounter } = setup();
  // ASSERT
  expect(getCounter(0)).toBeInTheDocument();
});
test("Counter should increment when button is pressed", async () => {
  // ARRANGE
  const { getCounter, increment, user } = setup();
  // ACT
  await user.click(increment);     #6
  // ASSERT
  expect(getCounter(1)).toBeInTheDocument();
});
test("Counter should decrement when button is clicked", async () => {
  // ARRANGE
  const { getCounter, decrement, user } = setup();
  // ACT
  await user.click(decrement);
  // ASSERT
  expect(getCounter(-1)).toBeInTheDocument();
});
test("Counter should not update value if passed start property changes", () => {
  // ARRANGE
  const { getCounter, rerender } = setup(10);
  // ACT
  rerender(20);     #7
  // ASSERT
  expect(getCounter(10)).toBeInTheDocument();
});

Shows how a shared setup function centralizes queries, user-event initialization, and rerender support. section 10.2.1

How React in Depth covers it

How deep it goes
Demonstrates comments, asynchronous clicks, rerendering, and reusable setup, but does not develop broader test architecture.
Examples
Uses a click counter with initial values, increment and decrement buttons, and changed start properties.
What it assumes
Assumes React component testing needs; adds Vitest and Testing Library infrastructure to an application created with Vite.
What you can do after
Apply it via Vitest and Testing Library to write readable tests for static, interactive, and stateful React components.
How it connects
Connects component rendering, user-event interactions, rerendering, custom test setup, mocking, and regression testing.

Also explained in

Compare how 6 books explain arrange act assert pattern →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of React in Depth. The section links point to the parts of the book it draws on.