Monorepo Testing Strategy: Bazel, Nx, Turborepo (2026)
Monorepos centralize multiple projects under one repository — shared dependencies, unified tooling, cross-project refactoring. Testing at scale becomes a discipline: run only what changed, parallelize
Monorepos centralize multiple projects under one repository — shared dependencies, unified tooling, cross-project refactoring. Testing at scale becomes a discipline: run only what changed, parallelize across projects, report clearly which project broke. This guide covers the strategy.
Why monorepos for testing
- Single source of truth: version bumps, refactors across projects happen once
- Cached builds / tests: run only affected projects on PR
- Unified tooling: one test runner, one linter, one formatter
- Atomic changes: cross-project refactor in one PR
The flip side: tests at scale get slow, flaky, and hard to own if not managed well.
Tools
Nx (JS / TS focused)
- Dependency graph tracking
- Cache test results
- Affected-only runs:
nx affected --target=test - Project tags for filtering
Turborepo
- JS / TS focused, simpler than Nx
- Caching, incremental builds
- Good for smaller orgs
Bazel
- Language-agnostic
- Strong caching, hermetic builds
- Steep learning curve
- Google / enterprise scale
pnpm / yarn workspaces
- No test orchestration, but basic monorepo
- Pair with Turborepo or Nx
Lerna
- Legacy, still used
- Simpler than Nx
Test organization
Per-project
packages/
ui/
src/
test/
package.json
api/
src/
test/
package.json
mobile-app/
android/
ios/
test/
Each project has its own tests. Shared utilities in a dedicated test package.
Dependency graph awareness
api depends on shared-types. Change in shared-types → run api tests. Change in api → skip ui tests (no dependency).
nx affected --target=test --base=origin/main
Runs only what affected-or-dependent projects need.
CI parallelism
Shard across runners:
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: nx affected --target=test --parallel=4 --shard=${{ matrix.shard }}
4-way shard cuts wall-clock time 4x.
Caching
Local cache
Tests run once; subsequent runs use cache if inputs unchanged. Instant turnaround on unchanged code.
Remote cache
Cache shared across team and CI. First PR runs tests; second PR (same code) uses cache.
# Nx Cloud
nx connect-to-nx-cloud
Commercial: Nx Cloud, Turborepo Remote Cache, Bazel BuildBuddy.
Metrics
Track:
- Test runtime per project
- Flaky test rate per project
- Cache hit rate
- Test-added / test-removed per PR
Visibility drives improvement.
Anti-patterns
Monolithic test run
npm test runs 10,000 tests every PR. Slow, expensive.
Fix: Affected-only.
Flaky tests not owned
Every project shares the flaky blame. Tests never fixed.
Fix: Owner in CODEOWNERS file; flake rate attributed per project.
Unified test framework forced
One project prefers Vitest, another Jest. Forced conformity causes friction.
Fix: Flexibility per project within unified orchestration.
Copy-paste setups
Each project has its own test config, config drifts, updates manual.
Fix: Shared config package; each project imports.
No isolation
Tests share state, one breaks another.
Fix: Per-test isolation (fresh DB, fresh filesystem, etc.).
Cross-project tests
Some tests span projects (integration, contract). Options:
- Dedicated integration-tests project at root
- Lives in
packages/e2e/ - Depends on all projects; runs on full-repo test
Migration
Single repo → monorepo
- Establish tooling (Nx / Turborepo / Bazel)
- Migrate first 2-3 projects
- Validate affected-only works
- Migrate remaining
- Set CI to affected-only for merges to main
Expect 3-6 months for large repos.
How SUSA fits
SUSA can run per-app exploration in a monorepo. Each app / web-domain gets its own exploration target. Affected logic can route:
- Changed backend → SUSA on frontend tests backend indirectly
- Changed mobile code → SUSA on that specific build
Monorepo CI orchestrates SUSA alongside other tests:
jobs:
susa-android:
if: ${{ needs.affected.outputs.android == 'true' }}
steps:
- run: nx build mobile-android
- run: susatest-agent test app.apk --persona curious --steps 100
Scaling checklist
- [ ] Affected-only runs on PR
- [ ] Shard parallel in CI
- [ ] Remote cache enabled
- [ ] Per-project flake rate tracked
- [ ] CODEOWNERS for test ownership
- [ ] Test runtime budget per project
- [ ] Integration tests in dedicated package
Monorepos scale testing well when managed. Wrong setup makes them worse than separate repos. Invest in tooling.
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free