Exhaustive testing patterns for multi-tenant SaaS — unit tests (Vitest), integration tests, E2E tests (Playwright), multi-tenant isolation tests, negative security tests, test data factories, and CI integration. Covers both NestJS backend and Next.js frontend. Trigger when writing tests, setting up test infrastructure, reviewing test coverage, or asking about testing strategies for multi-tenant applications.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Exhaustive testing patterns for multi-tenant SaaS — unit tests (Vitest), integration tests, E2E tests (Playwright), multi-tenant isolation tests, negative security tests, test data factories, and CI integration. Covers both NestJS backend and Next.js frontend. Trigger when writing tests, setting up test infrastructure, reviewing test coverage, or asking about testing strategies for multi-tenant applications.
Fourth Layer: Isolation Tests (Cuts Across All Three)
Isolation tests verify that workspace A cannot see workspace B data. They run at integration level (real DB) but are so critical they get their own category, dedicated files, and mandatory CI gate.
ANY module shipped without isolation tests = BLOCKED from merge.
WHEN TO WRITE EACH TEST TYPE
Unit Tests — ALWAYS
Write unit tests for:
Every service method (business logic)
Every utility/helper function
Every Zod schema (valid + invalid inputs)
Every React hook (TanStack Query wrappers, Zustand stores)
Every pure component (rendering, conditional UI)
Do NOT unit test:
Controllers (test at integration level with supertest)
Repository methods (test at integration level with real DB)
CSS/styling (test visually with Playwright screenshots)
Integration Tests — For Every API Endpoint
Write integration tests for:
Every controller endpoint (HTTP method + route + response shape)
Every repository method (real DB, real queries)
Every BullMQ processor (real job execution, mock external APIs)
Every guard/middleware (TenantContextGuard, auth guards)
Every endpoint's workspace boundary (isolation test)
MULTI-TENANT ISOLATION TESTS (THE MOST CRITICAL SECTION)
See multi-tenant-testing.md for the complete isolation test framework, templates,
and all 6 negative security tests as runnable code.
Why Isolation Tests Are #1 Priority
A broken feature is a bug. A broken tenant boundary is a security incident, a compliance violation, and potentially a company-ending event. Every endpoint that touches data must prove it cannot leak across tenant boundaries.
describe('ClassName or ModuleName')
describe('methodName')
it('should [expected behavior] when [condition]')
Examples:
describe('RecipientsService')
describe('create')
it('should create recipient when under limit')
it('should throw PaymentRequiredException when limit exceeded')
it('should log audit event on creation')
describe('bulkDelete')
it('should delete only IDs belonging to the domain')
it('should return affected count')
it('should enqueue BullMQ job when count exceeds 1000')
Frontend (Vitest + Testing Library)
describe('ComponentName or HookName')
it('should render [element] when [condition]')
it('should call [handler] when [user action]')
it('should show [feedback] after [mutation]')
E2E (Playwright)
test.describe('Feature or User Journey')
test('should [complete action] as [role]')
MOCK PATTERNS: WHEN TO MOCK, WHEN NOT TO
Mock These (Unit Tests)
Database repositories
External API clients (email senders, storage, SSO providers)
Redis client
BullMQ queues
AuditService (fire-and-forget behavior is hard to assert otherwise)
Date.now() / randomUUID() when deterministic output matters
Do NOT Mock These (Use Real Implementations)
Zod schemas (test validation logic directly)
Utility/helper functions (they are pure — no side effects)
TanStack Query hooks (wrap in QueryClientProvider, mock the API layer)
Zustand stores (test directly, they are synchronous)
NestJS guards (test at integration level with real guard chain)
Mock Boundary Rule
Unit test: mock at the REPOSITORY boundary
Integration test: mock at the EXTERNAL SERVICE boundary (real DB, real Redis)
E2E test: mock NOTHING (full stack, real DB, real Redis, real queues)
MASTER CHECKLIST — Run Before Shipping Any Feature
Unit Tests
Every service method has >=1 happy path + >=1 error path test
Every Zod schema tested with valid + invalid inputs
Every React hook tested with QueryClientProvider wrapper
Every Zustand store action tested
Mocks are at repository boundary (not deeper, not shallower)
No any types in test files
Test names follow should [behavior] when [condition] pattern
Integration Tests
Every controller endpoint tested (status code + response shape)
Auth required: 401 without token
Validation: 400 for invalid input
Not found: 404 for missing resource
Conflict: 409 for duplicates
Real database used (test container or CI service)
Data cleaned up after each test (truncate or transaction rollback)
Multi-Tenant Isolation (MANDATORY)
Every GET endpoint: WS-A token cannot fetch WS-B data
Every POST endpoint: cannot create data in wrong workspace
Every PATCH endpoint: cannot update data in wrong workspace
Every DELETE endpoint: cannot delete data in wrong workspace
Bulk operations: cannot touch cross-workspace records
Company endpoints: aggregate correctly, do not leak individual workspace data
Domain-level: domain-A query returns zero domain-B recipients
E2E Tests
Auth flow: login, session persist, logout
Workspace switch: data refreshes, no stale data visible
CRUD: create, edit, delete via UI — verify in data table
Data table: search works, filters work, sort works
Bulk actions: select multiple, execute, verify result
Import: upload CSV, map columns, review, commit
Progressive complexity: UC1 user sees no multi-tenant UI
CI Pipeline
Unit tests pass in <2 minutes
Integration tests pass with real DB (postgres service in CI)
E2E tests pass with Playwright (artifacts uploaded on failure)
'should return workspace count for progressive complexity'
() =>
setState
workspaces
id
'ws-1'
name
'Tata Steel'
companyId
'c-1'
id
'ws-2'
name
'Tata Motors'
companyId
'c-1'
const
getState
expect
workspaces
length
toBe
2
// UC3 features should be visible
0
path
toContain
'email'
it
'should reject empty firstName'
() =>
const
CreateRecipientSchema
safeParse
email
'test@test.com'
firstName
''
lastName
'User'
expect
success
toBe
false
it
'should apply default status when not provided'
() =>
const
CreateRecipientSchema
parse
email
'test@test.com'
firstName
'Test'
lastName
'User'
expect
status
toBe
'active'
describe
'RecipientFilterSchema'
() =>
it
'should accept empty filter (all defaults)'
() =>
const
RecipientFilterSchema
safeParse
expect
success
toBe
true
it
'should coerce page/limit to numbers'
() =>
const
RecipientFilterSchema
parse
page
'2'
limit
'25'
expect
page
toBe
2
expect
limit
toBe
25
it
'should reject negative page numbers'
() =>
const
RecipientFilterSchema
safeParse
page
1
expect
success
toBe
false
string
companyId
string
tokenA
string
// JWT for workspace A admin
tokenB
string
// JWT for workspace B admin
companyToken
string
// JWT for company admin
/**
* Creates a full multi-tenant test hierarchy:
* Company (Tata Group)
* ├── Workspace A (Tata Steel) with Domain A (tatasteel.com)
* └── Workspace B (Tata Motors) with Domain B (tatamotors.com) + Domain C (tatamotors.co.in)
*/