Hero Background

Next-Gen App & Browser Testing Cloud

Trusted by 2 Mn+ QAs & Devs to accelerate their release cycles

Next-Gen App & Browser Testing Cloud
TestingCloud Testing

SaaS Testing: How to Test Multi-Tenant Applications

SaaS testing is web application testing plus three problems that only exist because customers share one instance. Here is how to cover tenant isolation, entitlement states, and a release that never stops shipping.

Author

Siddhant Sinha

Author

Author

Sushobhit Dua

Reviewer

Last Updated on: August 25, 2026

A support ticket arrives saying a customer can see an order that is not theirs. You pull the API response and it looks fine: the tenant field at the top says their account, the record count is plausible, nothing in code review looked wrong. Four levels down, inside a linked sales rep object, sits another company's identifier.

That bug is the one class of failure that only exists because your customers share an instance, and it is the reason SaaS testing is not simply web testing with a subscription attached. This guide covers the three things the shared model adds to your test plan, with a runnable assertion for the one that matters most.

TL;DR

  • How to test a SaaS application: cover it as an ordinary web app, then add three layers the shared model demands - tenant isolation assertions on every scoped endpoint, entitlement checks across seeded billing states, and per-commit gates in place of a release candidate.
  • SaaS tenant isolation: a request authenticated as one tenant must never surface another tenant's data. OWASP rates the underlying flaw class exploitability Easy and prevalence Widespread. Unique to multi-tenant products: yes.
  • Why SaaS isolation bugs pass code review: the response looks right. The top-level tenant id matches the caller and the leak sits several objects deep. Caught by checking the top level: no. Caught by walking the payload: yes.
  • The SaaS isolation assertion: collect every value held by a tenant-identifier key anywhere in the response and fail if any belongs to a known other tenant. Catches nested leaks: yes. Catches cross-tenant aggregates: no.
  • Cross-tenant aggregates in SaaS: a summary reporting counts across every account leaks information without printing any foreign tenant id. Needs a separate explicit assertion: yes.
  • SaaS entitlement states are fixtures, not scenarios: seed trial, active, past-due, cancelled, and each plan tier, then run identical assertions across all of them. Most common failure: downgrades and expiries, where access should be revoked.
  • SaaS has no release candidate: every tenant runs the latest deploy. Gate on the commit rather than the release, add a post-deploy smoke suite, and roll back with a feature flag rather than a redeploy.
  • Sizing the SaaS browser matrix: draw it from your own product analytics rather than the market. Run everything above a meaningful traffic share each release and rotate the long tail on a schedule.

What Actually Makes SaaS Testing Different

Most of a SaaS test plan is ordinary web application testing, and treating the whole thing as special wastes effort. Exactly three properties of the delivery model change what you have to cover.

PropertyWhat it changesThe test layer it demands
Shared instanceOne codebase and one database serve every customerTenant isolation assertions on every tenant-scoped endpoint.
EntitlementsThe same build behaves differently by plan and billing statusSeeded subscription fixtures, with the same assertions run against each.
Continuous deploymentNo versioned release; every tenant is on the latest deployPer-commit gates, post-deploy smoke checks, and flag-scoped rollout.

Everything else, forms, navigation, responsiveness, accessibility, performance, is the same work you would do on any web product. The rest of this article covers the three rows, because they are what a generic plan misses. For the broader organisational version of this problem, see enterprise application testing.

Tenant Isolation Is the Defining Risk

Every other SaaS bug costs you a support ticket. This one costs you the customer.

The underlying flaw class is well documented: OWASP's API1:2023 Broken Object Level Authorization entry rates its exploitability as Easy and its prevalence as Widespread, noting the issue "is extremely common in API-based applications because the server component usually does not fully track the client's state, and instead, relies more on parameters like object IDs".

OWASP is direct about the consequence too: unauthorized access to other users' objects "can result in data disclosure to unauthorized parties, data loss, or data manipulation", and under some circumstances full account takeover.

In a multi-tenant product that description is not abstract. The object ID is a tenant's record, and the four ways it leaks in practice are:

  • The missing scope clause - A query filters by record id but forgets the tenant predicate. Guessing an id returns someone else's row.
  • The nested relation - The primary records are scoped correctly, but an included relation such as an assigned owner or linked account is fetched without the same filter.
  • The cross-tenant aggregate - A count, total, or average computed over the whole table rather than the caller's slice. No foreign identifier ever appears, and information still leaks.
  • The cached response - A response cached without the tenant in the cache key, served to whoever asks next.

Only the first is reliably caught by conventional tests, because it is the only one where the obviously wrong records come back.

Test infrastructure that does not break, from TestMu AI

Writing the Isolation Assertion

The useful assertion is not "did I get my records back". It is does any part of this payload reference a tenant that is not me. That requires walking the whole response rather than checking its top level.

const TENANT_KEY = /^(tenant|org|account|workspace|company)(_|-)?id$/i;

// Walk any response shape and collect every value held by a tenant-identifier key.
export function tenantRefsIn(value, found = new Set()) {
  if (value === null || typeof value !== 'object') return found;
  if (Array.isArray(value)) {
    value.forEach((v) => tenantRefsIn(v, found));
    return found;
  }
  for (const [k, v] of Object.entries(value)) {
    if (TENANT_KEY.test(k) && (typeof v === 'string' || typeof v === 'number')) found.add(String(v));
    tenantRefsIn(v, found);
  }
  return found;
}

export function assertScopedTo(response, tenantId, knownOtherTenants = []) {
  const refs = [...tenantRefsIn(response)];
  const foreign = refs.filter((r) => r !== tenantId);
  const confirmed = foreign.filter((r) => knownOtherTenants.includes(r));
  return { refs, foreign, confirmed, leaked: confirmed.length > 0 };
}

Two design choices in that are worth explaining, because the obvious versions fail.

It compares against known other tenants rather than flagging anything that is not yours. A looser key pattern matching /account/ collects the value of a field like account_count: 37 and reports 37 as a foreign tenant. That false positive gets the check disabled within a week, so the pattern anchors on identifier keys and the comparison uses a real fixture list.

const NESTED_LEAK = {
  tenant_id: 'acme',                                    // top level looks correct
  orders: [
    { id: 1, tenant_id: 'acme', total: 100 },
    { id: 2, tenant_id: 'acme', total: 250,
      assigned_rep: { name: 'Dana', org_id: 'globex' }, // the leak, three levels down
    },
  ],
};

test('catches a foreign tenant id nested three levels down', () => {
  const r = assertScopedTo(NESTED_LEAK, 'acme', ['globex', 'initech']);
  assert.equal(r.leaked, true);
  assert.deepEqual(r.confirmed, ['globex']);
});

test('a correct top-level tenant_id does not make the response safe', () => {
  assert.equal(NESTED_LEAK.tenant_id, 'acme');
  assert.equal(assertScopedTo(NESTED_LEAK, 'acme', ['globex']).leaked, true);
});

Running the full file, which also covers the clean case and the false-positive trap, gives this actual output:

$ node --test tenant-isolation.test.mjs
ok 1 - a correctly scoped response references only the caller tenant
ok 2 - catches a foreign tenant id nested three levels down
ok 3 - a correct top-level tenant_id does not make the response safe
ok 4 - matching on key NAME alone would flag account_count as a leak
ok 5 - an id scan cannot see a cross-tenant aggregate at all
# tests 5
# pass 5
# fail 0
# duration_ms 145.5754

Five assertions in 146 milliseconds. Test five is the honest limit of the technique: a summary object reporting account_count: 37 across every customer contains no foreign tenant id at all, so this scan returns clean on a genuine leak. Aggregates need their own explicit assertion that the number equals what the caller should see.

Run the scan as a shared helper across every tenant-scoped endpoint rather than writing it per test. The endpoints nobody thinks to cover are exactly where the missing scope clause survives.

Entitlement and Billing States

In a SaaS product the same build behaves differently for every account, because plan tier and billing status gate what is available. That turns entitlement into a matrix, and matrices are cheap to test only if you treat the states as fixtures.

  • Seed one account per state - Trial, active, past-due, cancelled, and one per plan tier. Create them programmatically so the suite can reset them, never by hand in a shared environment.
  • Run identical assertions across all of them - The same list of features, each expected available or blocked. A per-state bespoke test is how coverage silently diverges.
  • Test the transitions, not just the states - Downgrade and expiry are where entitlement bugs live, because removing access is implemented later and less carefully than granting it.
  • Assert the block, not just the absence - A hidden menu item is not access control. Call the API directly as the downgraded account and confirm it is refused.
  • Cover the grace period explicitly - Past-due usually means degraded rather than blocked, and exactly what stays available is a product decision nobody wrote down.

The fourth point is where entitlement testing meets isolation testing: both fail when the interface hides something the API still returns.

Detect and fix flaky tests with TestMu AI

Testing When There Is No Release

Most test strategy assumes a release candidate: a build that gets frozen, tested, signed off, and shipped. SaaS removes that. Every tenant is on the latest deploy, there is no version to hold, and no window in which the software stops changing.

The gate moves from the release to the change:

  • Per-commit checks that finish fast - Unit, integration, and the isolation scan on every pull request. If this stage takes more than a few minutes people learn to ignore it.
  • A post-deploy smoke suite against production - Because there was no staging release to validate, the first real signal comes after the deploy. Keep it small and keep it fast.
  • Feature flags as the actual rollback - Turning a flag off for the affected tenants is faster and safer than a redeploy, which makes flag state something your tests need to cover in both positions.
  • A canary tenant set - Expose a risky change to a handful of internal or volunteer accounts first, and assert the same journeys against them before widening.
Note

Note: Continuous deployment only works when the checks after each deploy are fast enough to act on. TestMu AI runs your existing Selenium, Cypress, and Playwright suites in parallel across the browser matrix your customers actually use. Try TestMu AI free!

Sizing the Browser and Device Matrix

A SaaS product is used in whatever browser its customers already have open, which is not the browser your team develops in. The matrix question is real, and the common failure is answering it by guessing rather than by looking at your own analytics.

Pull the browser, OS, and device distribution from your product usage data, then split it: everything above a meaningful share of traffic runs on every release, and the long tail runs on a schedule. That keeps the per-commit suite fast while still covering the combinations that actually appear in support tickets.

Running that matrix in-house is the part that does not scale, which is what TestMu AI's Automation Cloud exists for: it runs your existing Selenium, Cypress, Playwright, and Puppeteer scripts across 3,000+ real browser and OS combinations in parallel, with no local grid to patch and no proprietary DSL to migrate to. Network logs, console logs, video, screenshots, and command logs are captured on every run, which matters more than usual here, because a tenant-specific bug reproduced once needs enough artifacts to diagnose without asking the customer to try again.

If you are still choosing tooling for the wider stack, our roundup of SaaS testing tools covers the categories and where each fits.

A Practical SaaS Test Plan

LayerRuns whenFails the build when
Unit and integrationEvery commitAny assertion fails. No external dependencies, so it stays fast.
Tenant isolation scanEvery commit, on every tenant-scoped endpointAny response references a known other tenant, or an aggregate is unscoped.
Entitlement matrixOn merge to mainA blocked feature is reachable, or a paid feature is refused for a paying account.
End-to-end journeysOn merge, across the high-traffic matrixA core journey breaks on a configuration above your traffic threshold.
Post-deploy smokeAfter every production deployLogin, core read, and core write fail. Triggers a flag rollback, not a redeploy.

Row two is the only one that would not appear in a plan for a single-tenant application, and it is the one worth building first. A worked example of the dashboard side of this is in our guide to testing a SaaS dashboard.

Conclusion

Take your highest-traffic tenant-scoped endpoint and add the isolation scan to its existing test. It needs a seeded second tenant and about an hour, and it asserts something your current suite almost certainly does not: that nothing belonging to anyone else came back in the payload.

Then add the explicit aggregate assertion the scan cannot make for you, and seed the five subscription states so entitlement stops being tested by hand. Once those are in place, the remaining work is ordinary web testing run across a matrix drawn from your own analytics rather than guessed at.

Author

...

Siddhant Sinha

Blogs: 3

  • Linkedin

Siddhant Sinha is a Lead Member of Technical Staff at TestMu AI architecting Kane CLI, the command-line tool for browser automation from the terminal, where natural-language flows run in a real Chrome browser and return pass or fail with shareable proof. He has spent over three years at TestMu AI (formerly LambdaTest) building scalable platforms that run tests at scale on real Android and iOS devices. His expertise covers platform architecture, large-scale distributed systems, and CLI design, shaped by earlier cloud-native engineering at Semut.io, including building Elasticsearch as a service.

Reviewer

...

Sushobhit Dua

Reviewer

  • Linkedin

Sushobhit Dua is an Engineering Manager at TestMu AI (formerly LambdaTest), leading SmartUI, the visual regression and visual testing product. He manages the team that builds and ships SmartUI and maintains and cuts releases of the open-source SmartUI CLI. He works primarily in Core Java, Spring Boot, and Gradle, and is an AMCAT Certified Software Engineer. He brings over 10 years of software engineering experience, with earlier work as a Software Engineer at ecare Technology Labs. Sushobhit owns the SmartUI roadmap and the engineering decisions behind it.

Add to Google preferred sources Icon

Add to Google preferred sources

Open in ChatGPT Icon

Open in ChatGPT

Open in Claude Icon

Open in Claude

Open in Perplexity Icon

Open in Perplexity

Open in Grok Icon

Open in Grok

Open in Gemini AI Icon

Open in Gemini AI

Copied to Clipboard!
...

3000+ Browsers. One Platform.

See exactly how your site performs everywhere.

Try it free
...

Write Tests in Plain English with KaneAI

Create, debug, and evolve tests using natural language.

Try for free

SaaS Testing FAQs

Did you find this page helpful?

More Related Blogs

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests