Testing tenant isolation in Laravel SaaS proves that one organization cannot read, change, infer, or export another organization’s data—even through guessed identifiers, queued jobs, files, broadcasts, reports, or support tools. A tenant_id column or global scope is useful, but neither is proof. Tests must cross every boundary where context is created, propagated, or deliberately bypassed.
This guide assumes a multi-tenant application and focuses on adversarial test design. Database-per-tenant, schema, and shared-column architectures change some failure modes, but authorization, jobs, storage, integration, and support surfaces still require evidence.
State isolation invariants
Write rules that may never be violated: every business record has an owning tenant; actions require current membership; public identifiers cannot bypass policies; jobs restore explicit tenant context; files require authorization; and cross-tenant administration is deliberate and audited.
Identify genuinely global resources such as a public plan catalogue. Keep them distinct from tenant data. Ambiguity invites developers to remove a boundary to make a feature work.
Build adversarial factories
Every isolation test should create at least two tenants with users and similar resources. Use adjacent identifiers and sometimes identical names so a test cannot pass because a lookup accidentally selects the only record in the database.
Create pending or revoked membership and a user belonging to two tenants with different roles. Test context switching, not only authentication. Make these factories cheap enough that every new feature can add a negative tenant case.
Attack HTTP routes directly
Attempt show, update, delete, download, bulk action, and nested operations using another tenant’s identifier. Assert the chosen 404 or 403 policy and prove no database mutation occurred. Hiding a link in Vue is not authorization.
Cover route model binding, public IDs, search, filters, exports, and nested relationships. /projects/{project}/tasks/{task} must prove that task belongs to the project and tenant rather than confirming both IDs exist independently.
Do not rely on a global scope alone
A global scope reduces ordinary query mistakes but can be removed, bypassed by a raw query, or omitted from an unusual relationship. Combine tenant context, policies, constrained services, and database constraints where practical. Limit withoutGlobalScopes to named, reviewed operations.
Test that normal web execution fails closed when tenant context is absent. Document exceptions for console, system jobs, and support tools instead of allowing a missing context to return every row.
Test creation and mass assignment
The client must not choose a trusted tenant_id. Submit another tenant’s ID on create, update, import, and copy operations and verify rejection or server-side replacement. Tenant ownership should come from authorized context.
Try associating an invoice with another tenant’s customer or attaching a user to a foreign project. Validate in requests, policies, and domain services and add composite keys or constraints where the database can prevent a cross-tenant relation.
Test aggregates and caches
Counts, sums, exists, charts, and reports can reveal foreign data without exposing rows. Give tenants different known values and assert exact results. Include tenant identity in cache keys and test that one tenant’s cached result cannot be served to another.
Review full-text search, analytics stores, and materialized views. Their documents need tenant identity and server-side filters. Test reindexing, deletion, and membership changes.
Test queues and scheduled jobs
A job should carry tenant identity and actor or system reason, restore context, and reauthorize at execution. Do not serialize a model and assume a global scope survives. Run jobs for two tenants sequentially in the same worker process to detect stale singleton or static context.
Cover retries, failed jobs, batches, chains, and scheduled commands. Restrict sensitive job payload visibility. The reliable webhook guide shows how inbound events also need verified account-to-tenant resolution before queued work.
Test file boundaries
Use tenant prefixes or stores, but enforce authorization on download rather than treating a path as a secret. Attempt to fetch another tenant’s object key, alter an identifier, and reuse an expired signed link.
Test thumbnails, background processing, exports, and temporary files. A private original can still leak if a generated derivative is written to public storage. Shared buckets require deliberate policies and scoped credentials.
Test broadcasting and notifications
WebSocket channels need authorization that maps channel identity to current membership. Attempt to subscribe to another tenant’s channel and inspect payload minimization. A channel name is not a credential.
Emails, notifications, and scheduled exports must select recipient, locale, and links under the correct tenant. Test a multi-tenant user and membership revocation between queueing and sending.
Test inbound and outbound integrations
Resolve tenant from a verified provider account or credential after signature validation, not from an arbitrary payload field. Send a correctly signed event for a different provider account and prove it cannot update the wrong tenant.
For outbound connections, verify callback, credential, and logs belong to the selected tenant. Test retry after configuration changes or tenant deletion. Do not permit untrusted callback URLs that can reach internal services.
Test support and administration
Cross-tenant support is the intentional exception and therefore the highest-risk path. Use a distinct role, visible impersonation, reason, time limit, and audit history. Tenant administrators should not inherit platform-wide access.
Test search result minimization, export restrictions, immediate role removal, and impersonation exit. Avoid shared support accounts that make actions unattributable.
Add structural safety tests
Architecture tests can assert that business models implement a tenant contract, migrations include expected ownership fields and indexes, and dangerous bypass services remain in an allowlist. Static review can flag withoutGlobalScopes, raw queries, generic cache keys, and public files.
These checks do not replace behavior tests, but they catch omitted layers. Every approved exception should point to a negative test that demonstrates its boundary.
Run against realistic infrastructure
SQLite may not reproduce production foreign keys, locks, JSON behavior, or collations. Run critical isolation tests against the production database family and under parallel execution when CI uses it. Keep fixtures synthetic and avoid sensitive output on failure.
The multi-tenant billing guide adds plan and entitlement boundaries to these data tests. SaaS development services can own architecture review, but the repository should retain an executable isolation suite.
Conclusion: attempt crossing every layer
Isolation evidence is not a screenshot. It is an adversarial suite spanning HTTP, policy, queries, aggregates, queues, files, broadcasts, integrations, and support. Start with two similar tenants and one test that fails when the boundary is removed. Request a Laravel SaaS tenant-isolation review with a tenant map and two sensitive journeys, without customer data or secrets.



