The Testing Pyramid is Dead. Draw Your Own Shape Instead

It's 2009.

You only know a few people who own an iPhone.

You're itching to finally use jQuery on one of your next projects.

When Mike Cohn publishes a new book, and shares his idea for a "testing pyramid":

"Write more unit tests!"

"Avoid end-to-end tests as much as possible!"

This pyramid helped devs figure out what tests to write, and we've been following this advice religiously ever since.

But now it's 2024 — and the world has changed a lot since then.

Let me show you why the testing pyramid has become less useful over time, and why we should all just draw our own shapes anyway.

It's time to bury the pyramid and draw something better.

The Almighty Testing Pyramid

A quick refresher on what the Testing Pyramid says:

  • Unit tests are fast and easy to write, so we should have lots of them
  • Integration (or service) tests are slower and harder, so we should have fewer
  • End-to-end tests are very slow and really difficult to write, so we should have relatively little

The original idea was written about by Mike Cohn in his book Succeeding with Agile, which was published in 2009. In the timeline of software development, that's ancient history. The way we write software has changed a lot since then.

He also comes from the C++ and Java world, which is heavily object-oriented, and faces different testing problems than we face in web development with Javascript (and now Typescript).

So here's the problem.

This might have worked well in the past, and for a particular tech stack, but we need to re-examine the pyramid to see if it still holds up today for web development.

The standard tooling that we get to use as web devs is really good, and this causes the pyramid to become more of a square.

How Modern Tooling Changes the Pyramid

First, let's look at end-to-end tests.

The WebDriver Protocol and Chrome DevTools Protocol have completely changed how we write and run end-to-end tests. Tools like Playwright and Cypress use these protocols to provide a much better testing experience compared to 10 years ago.

These modern tools have built-in waiting and retry mechanisms. When a test fails because an element isn't visible yet, they'll automatically wait and retry before failing. This makes tests much more reliable and less flaky.

Compare this to older tools like Selenium which still don't have these features built-in.

With Selenium, you have to manually implement waiting and retry logic, which is error-prone and time-consuming.

The result is that writing and maintaining end-to-end tests is much easier than it used to be. Tests are more reliable, debugging is simpler, and the development experience is significantly improved.

Now, writing e2e tests is easier, and running them can be quite fast.

So the narrow, pointy, top of the pyramid has gotten wider and less pointy because of this.

And those unit tests?

Well, Kent C. Dodds has done a great job of pointing out that adopting Typescript changes the shape of your testing strategy as well.

Before, we needed to write lots of defensive code to check if we passed weird things into methods. Now, we can use Typescript to make sure that never happens:

// Before
function add(a, b) {
// Check if a and b are numbers
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('Both arguments must be numbers');
}
return a + b;
}
// Now with Typescript
function add(a: number, b: number) {
// No need to check if a and b are numbers
return a + b;
}

We still need to test our business logic, but by using Typescript, we can write fewer unit tests.

So the wide, flat, base of the pyramid has gotten narrower. Our testing pyramid is becoming more of a square.

But all of this is rather beside the point.

The thing that you should be doing is drawing the testing shape that's right for your project, not borrowing an idea from someone else.

You need to draw your own shape

Let's think about version control for a moment.

While there are many options available (Git, SVN, Mercurial), there's a clear winner. Git has become the de facto standard, and for good reason. If you're starting a new project today, there's really no debate about what to use.

(Unless you're just FTPing files to a server, but then I can't really help you.)

Some technical decisions are like this. While multiple options exist, we've collectively figured out the best approach.

This can only happen when there is a best approach.

However, testing strategy isn't one of those things. It's more like city planning — there's no single "correct" way to design a city. It depends on geography, population, climate, culture, and more.

What works perfectly in one city might completely fail in another. This is why we have so many different kinds of cities in the world.

The same is true for streaming services. You can't say that Netflix is the "best" choice, because it entirely depends on what you want to watch, whether you're already paying for Prime, and a bunch of other factors.

And so we have dozens of them, instead of a single streaming service that rules them all.

We can see this happening with testing strategies, too. There is a proliferation of testing shapes to choose from:

  • Pyramid: the classic approach
  • Diamond
  • Honeycomb
  • Trophy
  • Ice cream cone
  • Crab

Yes, these are all real testing shapes that people have written about, which means that there are probably more being used that aren't written down.

The reason why we can't agree on what a good testing strategy looks like is because there isn't a single correct answer.

We each need to figure out and decide what the best approach is for our project.

Drawing Your Own Shape

This is where things get challenging.

It's the part where I say, "it depends", and fail to give you what you're looking for — a single, correct answer you can use in your project right now.

What I can give you is a framework to help you figure out which shape is right for your project.

There are several dimensions to consider. Each one of these can affect your testing strategy. And because these things can change over time, your testing strategy should change over time, too.

Here are some of them:

  1. Team size — A small team might not have the same capacity to write lots of unit tests as a large team. The focus may end up on e2e tests instead.
  2. Team structure — If you have a dedicated QA team, they may focus on e2e tests and thorough manual testing. This means you have more time for other tests.
  3. Architecture — Microfrontends and microservices need more integration tests than monoliths do, and fewer unit tests.
  4. Risk tolerance — Startups are trying desperately to find product-market fit, and writing detailed unit and integration tests for features you may kill off next week is a waste of time, so the focus shifts to a few e2e tests to ensure the core functionality works.

I'm sure you can think of many more that affect your own testing strategy.

This is as far as I can help you (at least in an article format).

You need to do the work to think about your own project, and figure out what the right shape is for you.

Maybe it's a circle, or maybe it's a sphere? Maybe what you need is a testing Klein bottle.