Software Testing

Why the Test Pyramid is a Misleading Guide to Software Testing in 2025

October 18, 20249 min read
Software Testing pyramid image
source https://martinfowler.com/bliki/TestPyramid.html

Why the Test Pyramid is a Misleading Guide to Testing - Software Testing of Modern Frontend and Backend apps

TL;DR: The structure and levels of the test pyramid should be tailored to your application—it doesn’t necessarily have to resemble a pyramid. However, certain anti-patterns should be avoided.

In this guide, I aim to address the types of testing specific to web applications, particularly in light of the rising popularity of Single Page Applications (SPAs) and microservices. The terminology in this area is often inconsistent, and I'll discuss how we can contextualize these practices within the well-known test pyramid, which will also be covered here. My goal is to explore what constitutes a sound approach to testing modern frontend and backend applications, without restricting the discussion to any particular tools.

After reading this article, I hope you'll understand why rigidly adhering to common testing patterns isn't always the optimal solution. I've conducted thorough research on this topic, but if you spot any inaccuracies or areas for improvement, feel free to contact me.

Introduction: The Purpose of Testing

The fundamental purpose of testing is to ensure that the software we develop meets client requirements and functions as expected. Often, we test to verify that recent changes haven't introduced new issues into the codebase. However, as our software grows in complexity, it becomes impractical to manually test every possible failure scenario. Although we could technically do it, the process would be so time-consuming and tedious that critical bugs would inevitably slip through due to human error. This is why automation becomes indispensable—it provides assurance that no mistakes have been made along the user journey while significantly accelerating the testing process. Both developers and QA engineers are responsible for writing tests, but manual testing remains necessary since automation cannot cover everything. Thus, QA manual testers still play a crucial role.

The (Not) Well-Known Three

Before diving into the test pyramid, let’s introduce the three foundational types of tests typically found in the pyramid framework.

Unit Tests

Unit tests are primarily written by developers and fall under the umbrella of white-box testing. The goal is to keep these tests simple, easy to debug, and isolated—meaning they should cover one method or function at a time. For example, testing three functions that return different Boolean values simultaneously could result in eight distinct test cases (2 * 2 * 2 = 8). Isolating unit tests allows for quick feedback because the components are tested independently in a controlled environment with mocked dependencies.

But what exactly constitutes a "unit"?

The definition of a unit varies depending on the programming paradigm. In object-oriented programming (OOP), a unit is often equated with a class, and the focus is on testing its public methods. Private methods are typically not tested directly because the class is treated as an indivisible unit. However, in other contexts, such as functional programming (FP), individual functions are treated as units, and the emphasis is on ensuring they return expected results based on input parameters.

Ultimately, there isn’t a universally correct answer. What matters is that the team agrees on a definition and remains consistent. In my view, what we define as a unit is simply a matter of consensus.

A useful guideline from Ham Vocke (on Martin Fowler’s website) suggests that unit tests should focus on observable behavior rather than reflecting internal code structure. In practice, this means asking:

“If I input values X and Y, does the result match my expectation (Z)?”

Rather than:

“If I input X and Y, does the method call Class A first, then Class B, and return the sum of their results?”

By focusing on behavior, we maintain flexibility for refactoring without breaking our tests. In terms of coverage, I recommend following Kent Beck's philosophy: write enough tests to feel confident your codebase isn’t prone to bugs.

2 test pyramid

image source

Integration Tests

Integration testing is where confusion often arises. What exactly should integration tests cover? In a React app, for instance, should they validate UI elements? What about in monolithic server-side applications or microservice architectures?

Definitions of integration testing vary, with some suggesting it tests processes and components, but these terms can have different meanings depending on the application. Integration tests can range from testing the interaction between client and server to verifying the proper function of multiple backend services. In the case of microservices, integration testing often focuses on validating the interactions between various service components.

For me, integration testing has traditionally referred to checking how different parts of the system, like the database and server, interact. However, in microservice-based architectures, integration tests often focus on validating the interplay between server-side services, which are sometimes referred to as component or service tests. Ultimately, the definition of integration testing varies by system architecture, and there’s no single "correct" approach.

End-to-End Tests (E2E)

Also known as UI tests, end-to-end testing involves testing a fully functioning instance of the application, which includes both the frontend and backend components. These tests simulate real-world user interactions within a browser.

Unlike unit or integration tests, E2E tests do not provide precise feedback. They may indicate a failure in the user interface, but identifying whether the problem lies in the frontend or backend requires further investigation. These tests are notoriously slow and expensive to maintain, so they are often limited to core user flows. E2E tests are typically more costly to develop, requiring 10 to 50 times the effort needed for manual test scenarios.

The key difference between E2E and integration tests lies in their scope: E2E tests validate the system as a whole, including all layers of the stack (e.g., UI, business logic, backend, etc.). In contrast, integration tests may focus on a narrower range of components.

The Test Pyramid

The test pyramid, first introduced by Mike Cohn in his book Succeeding with Agile, advocates for a large base of unit tests, followed by integration (service) tests, and capped with a smaller number of E2E (UI) tests. These tests are all automated and functional.

1 test pyramid

image source

In some contexts, the test pyramid can be seen as an error filter—defects undetected at one layer are likely to be caught at the next.

3 test pyramid

the testing pyramid as a filter, image source

The pyramid was introduced as a response to the proliferation of E2E/UI tests, which, although comprehensive, can be slow, brittle, and difficult to debug. By emphasizing unit tests, which are faster and more reliable, the pyramid reduces reliance on expensive UI testing, while still ensuring adequate coverage through a small number of E2E tests.

Anti-Patterns — The Ice Cream Cone

The ice cream cone anti-pattern is a common issue in projects where testing strategies are inverted—resulting in a large number of slow, costly UI tests and insufficient unit test coverage. This pattern leads to bloated and slow build pipelines, which frustrates developers and delays releases. Over time, the codebase becomes so fragile that even small changes introduce significant risk, causing a detrimental impact on the overall development process.

4 test pyramid

the ice-cream cone anti-pattern, image source

To avoid this pitfall, it’s crucial to prioritize writing unit tests early in the project lifecycle. While upper management pressure may push for faster releases, investing in a robust testing strategy early on will save significant technical debt and reduce long-term costs.

5 test pyramid

image source

Frontend Testing

6 test pyramid

two-level test pyramid may be a solution for frontend apps

For frontend applications, a two-tiered pyramid with unit and E2E tests may be sufficient. In this approach, logic, stores, and UI components are treated as distinct units. However, Kent C. Dodds’ Testing Trophy offers an alternative approach, where static tests (like linters and type checkers) form the base, followed by integration tests, and then E2E tests at the top. He emphasizes that integration tests strike a balance between speed, confidence, and cost, particularly for frontend applications.

7 test pyramid

The Testing Trophy for frontend apps, image source

Microservices Testing

More modern software development organisations have found ways of scaling their development efforts by spreading the development of a system across different teams. Individual teams build individual, loosely coupled services without stepping on each others toes and integrate these services into a big, cohesive system. The more recent buzz around microservices focuses on exactly that.

The Practical Test Pyramid, Ham Vocke, https://martinfowler.com/articles/practical-test-pyramid.html

In microservices architectures, testing strategies may shift. André Schaffer introduced the microservices testing honeycomb, which emphasizes integration testing between services, prioritizing the seamless interaction of these loosely coupled components. In this model, services are treated as units, and contract testing ensures that APIs between services function correctly.

8 test pyramid

The Microservices Testing Honeycomb, image source

Conclusion

Throughout this article, we've explored how the shape and structure of the test pyramid can vary based on the type of application you're testing. The essence of testing lies in understanding your application and its architecture before adhering to any particular pattern. The sooner you grasp the importance of a thoughtful testing strategy, the smoother your development process will be as your application scales.

Sources

  • Mike Cohn, Succeeding with Agile: Software Development Using Scrum, Addison-Wesley Professional, 2009
  • Testing of Microservices, André Schaffer, https://labs.spotify.com/2018/01/11/testing-of-microservices/
  • What is functional testing with example?, Neha Sharma, https://www.quora.com/What-is-functional-testing-with-example/answer/Neha-Sharma-3421
  • 3 typy testów, które musisz zrozumieć, by robić efektywne TDD, Wojciech Zawistowski, https://it.esky.pl/programowanie/3-typy-testow-ktore-musisz-zrozumiec-by-robic-efektywne-tdd/
  • White-box testing, Wikipedia, https://en.wikipedia.org/wiki/White-box_testing
  • Black Box Testing, Software testing fundamentals, http://softwaretestingfundamentals.com/black-box-testing/
  • How deep are your unit tests?, Kent Beck, https://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests/153565#153565
  • Błędy popełniane przy automatyzacji testów, Radek Smilgin, http://testerzy.pl/baza-wiedzy/bledy-popelniane-przy-automatyzacji-testow
  • Test Pyramid, Martin Fowler, https://martinfowler.com/bliki/TestPyramid.html
  • Test Pyramid as a Risk Filter, Amit Kulkarni, https://amtoya.com/blogs/test-pyramid-as-a-risk-filter/
  • Piramida automatyzacji testów, Code Coverage, http://pisz-kod.pl/2018/06/07/piramida-automatyzacji-testow-code-coverage/
  • The testing pyramid, Automation Panda, https://automationpanda.com/2018/08/01/the-testing-pyramid/
  • The Evolution of the Testing Pyramid, James Willett, https://james-willett.com/2016/09/the-evolution-of-the-testing-pyramid/
  • Functional Testing Vs Non-Functional Testing: What’s the Difference?, https://www.guru99.com/functional-testing-vs-non-functional-testing.html
  • What is functional testing with example?, https://www.quora.com/What-is-functional-testing-with-example
  • Acceptance Testing, Software testing fundamentals, http://softwaretestingfundamentals.com/acceptance-testing/
  • Sanity Testing Vs Smoke Testing: Introduction & Differences, https://www.guru99.com/smoke-sanity-testing.html
  • Functional Testing, https://www.techopedia.com/definition/19509/functional-testing
  • Snapshot testing, https://jestjs.io/docs/en/snapshot-testing
  • Write tests. Not too many. Mostly integration., Kent C. Dodds, https://blog.kentcdodds.com/write-tests-not-too-many-mostly-integration-5e8c7fff591c
  • Performance Testing Tutorial: What is, Types, Metrics & Example, https://www.guru99.com/performance-testing.html
  • Load Testing Tutorial: What is? How to? (with Examples), https://www.guru99.com/load-testing-tutorial.html
  • Manual Testing Tutorial for Beginners: Concepts, Types, Tool, https://www.guru99.com/manual-testing.html
  • Software Testing