Yashveer Singh
Connect
<- All posts
Tech Debt and Refactoring12 min read

The Test Pyramid for SaaS: Unit, Integration, End to End

The test pyramid describes the ideal ratio of unit tests to integration tests to end to end tests in a healthy test suite. Unit tests form the base, integration tests sit in the middle, and end to end tests sit at the top. Most SaaS teams invert this unintentionally, building too many brittle end to end tests and too few integration tests. Understanding why the pyramid is shaped the way it is changes how you allocate testing effort.

Written by Yashveer Singh, founder of Yashveer Labs.

What you actually need to know

  • The pyramid shape reflects cost and reliability, not importance. End to end tests are the most valuable per test. They are also the most expensive to write, run, and maintain.
  • Most teams invert the pyramid accidentally. They write lots of end to end tests early and pay for it in slow CI and brittle builds.
  • Integration tests are the most underused tier in SaaS backends. They cover the database interactions, service boundaries, and business logic combinations that unit tests miss.
  • A three-minute fast suite for every commit and a longer end to end suite for pre-deploy is a workable structure for most teams.
  • Mock at the edges. Mock external services. Do not mock your own database.
  • Coverage numbers tell you where tests exist. They do not tell you whether the tests are protecting anything important.
Test tierSpeedReliabilityMaintenance costBest for
UnitVery fastVery highLowPure logic, validators, calculators
IntegrationMediumHighMediumDatabase flows, service boundaries, business logic
End to endSlowLowerHighCritical user journeys, full flow smoke tests
SnapshotFastMediumMediumStable UI components

The core argument

The test pyramid was described by Mike Cohn in 2009 and it remains accurate. The shape is not aesthetic. It is economic. Unit tests are cheap to write and fast to run. End to end tests are expensive to write, slow to run, and break every time the UI or infrastructure shifts. If you are going to have a lot of tests, most of them should be cheap.

The practical problem I see in most SaaS codebases is not that teams ignore testing. It is that they test at the wrong tier. They write end to end tests that cover business logic, when a faster integration test would do the same job in one tenth of the time. They skip integration tests on database-heavy code because writing them requires a test database setup, and that setup feels like a project.

The consequence is a suite that takes fifteen minutes to run and breaks on every UI refactor. Developers stop running it locally. CI becomes a waiting game. The suite that was supposed to build confidence starts to erode it.

The fix is deliberate tier assignment. Before writing a test, ask which tier this test belongs at. Can I test this behavior with a unit test? No, it requires database state. Can I test it with an integration test? Yes. Then write the integration test and skip the end to end test.

The three tiers in a SaaS context

Unit tests

Unit tests belong on pure logic. Validation rules, calculation engines, date utilities, permission-checking functions that take an object and return a boolean. Anywhere the input is small, the output is predictable, and the function does not depend on external state.

The discipline is to extract that logic into pure functions before testing it. A permission check that requires a database call is not a unit test candidate until you refactor out the pure logic.

Integration tests

Integration tests are the most valuable tier for SaaS backends, and the most commonly underdeveloped. They cover the code that actually does the work: creating a subscription, firing a webhook, handling a failed payment, building a user's permission set from database state.

The discipline here is to use a real test database. Mocking the database in an integration test is not an integration test. It is a slow unit test. Set up a test database in CI, seed it with fixtures, and test the actual SQL. The setup cost is a few hours. The value is that you can actually trust the tests.

End to end tests

End to end tests belong on the flows that are too important to leave unverified. Sign up, log in, complete the core transaction, cancel a subscription. Five to ten flows, not fifty.

The discipline is that end to end tests are the canary, not the net. They verify that the critical paths still function after a deploy. They are not designed to catch every edge case. Edge cases belong in integration tests.

What it costs to maintain each tier

Suite configurationDaily CI costMaintenance per quarterIncident catch rate
All end to end, few unitHigh, slow buildsHigh, brittle tests break constantlyMedium, misses edge cases
Heavy unit, few integrationLowLowLow, misses integration bugs
Balanced pyramidMediumMediumHigh when calibrated correctly
Pyramid with coverage gatesMediumMedium-highHigh

The numbers here are directional, not exact. The point is that a balanced pyramid is not the lowest maintenance option. It requires ongoing discipline. But it is the highest confidence option at the lowest cost over a two or three year timeframe.

What to look for in a healthy test suite

  • A fast suite that runs in under three minutes on every commit. If it takes longer, split it.
  • A separate slow suite for pre-deploy validation.
  • Integration tests that use a real database, not mocks.
  • End to end tests limited to the top five or ten user flows.
  • No flaky tests in the main suite. A flaky test teaches developers to ignore failures.
  • Failure messages that name the behavior that broke, not just the assertion.
  • A documented process for skipping a test with an expiry date.
  • Coverage gates on critical modules, not blanket coverage requirements.

Expert opinion

The teams I respect most do not have the highest coverage numbers. They have the clearest sense of what their tests are protecting. They know which tests are canaries, which are nets, and which are just expensive noise. That clarity is what produces a suite that actually speeds up delivery rather than slowing it down.

>

Yashveer Singh, founder of Yashveer Labs

How this played out on a real project

A SaaS client I worked with had a test suite that took eighteen minutes to run. The team had invested heavily in end to end tests early on because they worked well for the MVP stage. As the product grew, the suite became a liability. Developers were disabling tests to keep CI from timing out. The coverage number looked good. The incident rate was high.

We ran a two-day tech debt audit and found that the integration tier was almost empty. The database-heavy billing and subscription code, where most of the real bugs lived, had only end to end coverage. The pure logic functions had no tests at all.

The rebalancing took six weeks. We wrote integration tests for the billing and subscription flows first, using a real test database. Then we deleted the end to end tests that duplicated the integration coverage. The suite dropped from eighteen minutes to four. Incident rate dropped by roughly half in the two months after the change.

The companion post with a practical plan for adding tests to existing code is adding tests to a legacy codebase without going mad. On the question of whether the test suite itself has become a tech debt problem, why your test suite is slow and how to fix it is the natural follow-on.

Common mistakes

  1. Starting with end to end tests and staying there. The suite becomes slow and brittle before integration coverage exists.
  2. Mocking the database in integration tests. You are testing the mock, not the code.
  3. Setting a blanket coverage target. Coverage tells you where tests exist. It does not tell you whether they are testing the right things.
  4. Treating all flaky tests as acceptable with a retry. Flakiness is a symptom. Retrying hides it.
  5. Running the full suite on every commit. Split fast and slow suites early to keep the commit loop tight.
  6. Writing tests that test implementation rather than behavior. These tests break on every refactor and add no safety.
  7. Skipping the test database setup because it is inconvenient. The three-hour setup cost is worth less than a month of false confidence from mocked integration tests.

A six-week pyramid plan

  1. Week one. Audit the current suite. Count tests by tier. Identify which tier is over-represented and which is missing.
  2. Week two. Set up a test database in CI if one does not exist. Write three integration tests on the most critical data flows.
  3. Week three and four. Continue adding integration tests. One or two per working day on the modules that touch money, identity, or core product state.
  4. Week five. Review the end to end suite. Identify tests that duplicate integration coverage and remove them. Keep only the true smoke tests.
  5. Week six. Split the suite into fast and slow. Fast suite on every commit. Slow suite pre-deploy. Measure the commit loop time and target under three minutes.

For the broader discipline of managing the test investment as a team, the tech debt ledger post covers how to track the test gaps the same way you track other technical liabilities.

FAQ

Frequently asked

Author

Why you should hire Yashveer Singh for this

The kind of work this article describes is the kind of work I do every week. Production deployments, scaling decisions, the architecture choices that compound over years. I am Yashveer Singh, founder of Yashveer Labs. If you need this done, I do not need to be sold on the brief. Send me what you have and I will tell you what it actually takes.

Related reading