DesignDev.io logoDesignDev.io logo

  1. Home
  2. /From the Builder
  3. /The Testing Pyramid: What Is It Really?
← Back to home

The Testing Pyramid: What Is It Really?

Muhammad Athar

Muhammad Athar

Cover Image for The Testing Pyramid: What Is It Really?
Muhammad Athar

Muhammad Athar

January 13, 2026·6 min read
Updated April 26, 2026
Series:From the Builder
Level:Beginner
Tags:reactweb
Assumes:Basic JavaScript familiarity, has read or skimmed the React Foundations series

If you've been reading Mira Halsted's testing series and kept seeing references to "the testing pyramid" and "test budget" without a clear picture of what they actually mean in a real project — this is that picture. With a real component suite you can use to see each layer in action.


If you've read The testing pyramid for frontend — how to distribute your test budget by Mira Halsted, or any of the testing articles in her series, you've seen the pyramid mentioned as the framework for deciding how many tests to write at each level.

And if you're relatively new to testing, you might have thought: I understand there are different kinds of tests. I don't fully understand why a pyramid shape, why those ratios, or what I should actually be writing for the components I build every day.

This article answers all of that — with a real set of components and tests you can follow from scratch.


The problem testing solves

Before the pyramid, let's establish why we test at all.

You write a Button component. It works. You ship it. Three weeks later you change the styling and accidentally break the disabled state. A user submits a form twice because the disabled button wasn't actually preventing clicks. You find out from a bug report, not from your own testing.

Tests are a safety net. They tell you when something you changed broke something else. The earlier you catch a break — ideally before it ships — the cheaper it is to fix.

The testing pyramid is a framework for building that safety net efficiently. Not all tests are created equal in terms of cost, speed, and what they catch. The pyramid tells you which tests to write more of and which to write fewer of — and why.


The three layers

Think of the pyramid as a triangle with three horizontal bands:

Unit tests — wide base, many tests, fast, cheap. Integration tests — middle band, moderate tests, medium speed. End-to-end (E2E) tests — narrow top, few tests, slow, expensive.

The shape communicates the ratio: write lots of unit tests, a moderate number of integration tests, and a small number of E2E tests.

Let's define each layer concretely before talking about why.


Layer 1: unit tests

A unit test tests one thing in isolation — a function, a hook, a component — with everything it depends on either mocked or removed from the picture.

The key word is isolation. If your formatDate function breaks, your unit test for formatDate should fail. Nothing else should fail. The test is surgical.

Two tests. One function. The tests run in milliseconds. If formatDate breaks — because someone changed the locale, the format options, or the function logic — these tests fail instantly.

What unit tests are good at:

  • Catching logic bugs in pure functions
  • Testing edge cases quickly (empty string, zero, null, extreme values)
  • Testing hooks in isolation with renderHook
  • Running fast — a thousand unit tests in under two seconds

What unit tests can't catch:

  • Whether two correctly-written functions work together correctly
  • Whether a component renders the right output in a real browser context
  • Whether a user flow actually works end-to-end

Layer 2: integration tests

An integration test tests multiple units working together — without mocking everything away, but without a real browser or real network.

For React components, integration tests are what React Testing Library is designed for. You render a component (which renders its children, which might call hooks, which might call utilities), you interact with it the way a user would, and you assert the result.

This test doesn't mock formatDate. It tests the component as a unit that includes formatDate. If formatDate breaks, this test also fails — and that's correct. The integration test covers the seam between the component and its dependency.

What integration tests are good at:

  • Testing components that use hooks, utilities, and child components together
  • Catching bugs at the seams between units
  • Testing user interactions (clicking, typing, form submission)
  • Testing conditional rendering and state changes

What integration tests can't catch:

  • Whether the component looks right in a real browser
  • Whether a multi-page user flow works
  • Whether real API calls succeed

Layer 3: end-to-end tests

An E2E test drives a real browser through a real user journey. It opens a URL, clicks buttons, fills forms, and asserts outcomes — the way an actual user would.

This test covers something neither unit nor integration tests can: the full path from authentication to published article, including real routing, real form submission, real database writes.

What E2E tests are good at:

  • Verifying critical user journeys work end to end
  • Catching integration issues between frontend and backend
  • Testing things that only exist in a real browser (navigation, real network requests)

What E2E tests are bad at:

  • Speed — a single E2E test can take 10–30 seconds
  • Reliability — they're sensitive to network conditions, timing, and UI changes
  • Diagnosing failures — when they fail, it's not always obvious why

Why the pyramid shape

Now the pyramid makes sense. You want:

Lots of unit tests because they're fast, cheap, and surgical. A codebase with 500 unit tests that run in 3 seconds gives you continuous feedback as you code.

A moderate number of integration tests because they catch the bugs that unit tests miss — the seam bugs. But they're slower (10–100ms each instead of 1–2ms) and more complex to write.

A few E2E tests because they catch what integration tests miss — but they're 100x slower and 10x harder to maintain. You can't afford to have 500 of them. You pick your most critical user journeys: login, checkout, the core action your product is built around.

The pyramid is a budget allocation strategy. If you have an hour to write tests, the pyramid says: spend 60% of that hour on unit tests, 30% on integration tests, and 10% on E2E tests. You'll catch the most bugs per hour of testing invested.


Building the complete test suite for a real component

Here's a SubscribeForm component — a simple email subscription form — tested at all three levels. This is what the pyramid looks like in practice.

Unit tests — the isValidEmail function

Five tests. Pure function. No React involved. Runs in under 1ms. If the validation regex breaks, these fail immediately.

Integration tests — the SubscribeForm component

Six integration tests. Each covers a user-facing behaviour. These take 20–50ms each — fast enough to run on every file save.

E2E test — the full subscription flow

One E2E test. It covers what the integration tests can't: real routing, real API call, real database interaction. It runs in 5–10 seconds. You don't write ten of these — you write one for the happy path and trust the integration tests to cover the edge cases.


The ratio in practice

For this SubscribeForm:

  • 5 unit tests — the validation function, all edge cases
  • 6 integration tests — all user-facing behaviours
  • 1 E2E test — the critical happy path

That's a 5:6:1 ratio — roughly the pyramid shape. If you added ten more edge cases to the validation function, you'd add ten unit tests, not ten integration tests. If you added a new user flow (unsubscribe), you'd add integration tests for that flow and one E2E test for the critical path.

The pyramid scales with the codebase.


Where this leads

Vitest and React Testing Library — Mira Halsted's How to set up Vitest in a Vite or Next.js project from scratch and Component testing with React Testing Library — the mental model that makes it click are the practical next steps for setting up the unit and integration layers.

Playwright — How to set up Playwright for end-to-end testing in a Next.js app covers the E2E layer setup and the patterns that make E2E tests reliable rather than brittle.

The testing pyramid for frontend — Mira's closing article in the testing series, The testing pyramid for frontend — how to distribute your test budget, covers the strategic decisions that this article introduces through the lens of a real project with a real team.


This article was created for better understanding of Mira Halsted's testing series — particularly The testing pyramid for frontend and Component testing with React Testing Library. The SubscribeForm component above is yours to take — the test suite covers it at all three layers and gives you a starting point for the pattern in your own codebase.

Muhammad Athar is the founder and engineer behind DesignDev.io. He writes the "From the Builder" series — concept explainers triggered by the site's own articles, always grounded in something you can actually build.

Photo by Laurence Ziegler on Unsplash


More Stuff

Publishing Your First React Component to npm — the Complete Setup
April 1, 2026·3 min read

Publishing Your First React Component to npm — the Complete Setup

Most tutorials stop at "it works on my machine." This one doesn't. Publishing to npm is where your library becomes real — installable, versioned, and usable by anyone. Here's the complete setup, including the fields most developers get wrong in package.json and how to inspect what you're actually shipping before it goes live.

Muhammad Athar

Muhammad Athar

How to structure a UI library that scales — folders, exports, and naming conventions
April 1, 2026·3 min read

How to structure a UI library that scales — folders, exports, and naming conventions

The folder structure you pick on day one is the one you'll live with across 40 components. Get it wrong and you'll be fighting your own codebase by article 10. Get it right and adding a new component takes five minutes and follows a pattern every contributor can learn in one sitting.

Muhammad Athar

Muhammad Athar

DesignDev.io logoDesignDev.io logo

Explore

  • Home
  • Search
  • Authors
  • Series
  • About Us

Account

  • Sign in

Legal

  • Privacy Policy
  • Terms of Service

© 2026 DesignDev.io · All rights reserved

Built with Next.js · Tailwind · Sanity · Vercel