DEV Community

Alex Spinov
Alex Spinov

Posted on

Testing Library Has a Free Philosophy That Makes Your Tests Actually Useful

Most component tests break when you refactor. Testing Library tests survive because they test behavior, not implementation.

The Problem with Traditional Testing

// Enzyme style — tests implementation details
const wrapper = shallow(<Counter />);
expect(wrapper.state("count")).toBe(0);
wrapper.find(".increment-btn").simulate("click");
expect(wrapper.state("count")).toBe(1);
Enter fullscreen mode Exit fullscreen mode

Rename the CSS class? Test breaks. Change state shape? Test breaks. Switch to hooks? Test breaks. But the component still works.

Testing Library Approach

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

test("increments counter when button is clicked", async () => {
  render(<Counter />);

  expect(screen.getByText("Count: 0")).toBeInTheDocument();
  await userEvent.click(screen.getByRole("button", { name: /increment/i }));
  expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
Enter fullscreen mode Exit fullscreen mode

Rename CSS classes? Still passes. Refactor to hooks? Still passes. Change state management? Still passes. Because we test what the user sees and does.

Core Queries (Priority Order)

// 1. Accessible by everyone
screen.getByRole("button", { name: /submit/i });  // BEST — semantic
screen.getByLabelText("Email");                     // Form inputs
screen.getByPlaceholderText("Search...");           // Fallback
screen.getByText("Welcome back!");                  // Visible text
screen.getByDisplayValue("current input value");    // Form values

// 2. Semantic HTML queries
screen.getByAltText("Profile photo");               // Images
screen.getByTitle("Close");                         // Titles

// 3. Last resort (still better than class/id)
screen.getByTestId("custom-element");               // data-testid
Enter fullscreen mode Exit fullscreen mode

Async Queries

// Wait for element to appear
const message = await screen.findByText("Data loaded!");

// Assert element is NOT present
await waitForElementToBeRemoved(() => screen.queryByText("Loading..."));

// Wait for condition
await waitFor(() => {
  expect(screen.getByText("3 results")).toBeInTheDocument();
});
Enter fullscreen mode Exit fullscreen mode

User Events (Realistic Interactions)

import userEvent from "@testing-library/user-event";

const user = userEvent.setup();

// Type in input
await user.type(screen.getByLabelText("Email"), "user@test.com");

// Clear and type
await user.clear(screen.getByLabelText("Name"));
await user.type(screen.getByLabelText("Name"), "New Name");

// Select dropdown
await user.selectOptions(screen.getByLabelText("Country"), "US");

// Upload file
const file = new File(["hello"], "test.png", { type: "image/png" });
await user.upload(screen.getByLabelText("Avatar"), file);

// Keyboard
await user.keyboard("{Enter}");
await user.tab();
Enter fullscreen mode Exit fullscreen mode

Testing Patterns

Form Submission

test("submits form with valid data", async () => {
  const onSubmit = vi.fn();
  render(<ContactForm onSubmit={onSubmit} />);

  await user.type(screen.getByLabelText("Name"), "Alice");
  await user.type(screen.getByLabelText("Email"), "alice@test.com");
  await user.click(screen.getByRole("button", { name: /send/i }));

  expect(onSubmit).toHaveBeenCalledWith({
    name: "Alice",
    email: "alice@test.com",
  });
});
Enter fullscreen mode Exit fullscreen mode

Error States

test("shows validation errors", async () => {
  render(<ContactForm />);

  await user.click(screen.getByRole("button", { name: /send/i }));

  expect(screen.getByText("Name is required")).toBeInTheDocument();
  expect(screen.getByText("Email is required")).toBeInTheDocument();
});
Enter fullscreen mode Exit fullscreen mode

Works Everywhere

  • @testing-library/react — React
  • @testing-library/vue — Vue
  • @testing-library/svelte — Svelte
  • @testing-library/angular — Angular
  • @testing-library/preact — Preact

Same API, same philosophy, every framework.


Need robust testing for your web applications? I build developer tools and data pipelines. Email spinov001@gmail.com or check my Apify tools.

Top comments (0)