Skip to Content
PatternsChaos Testing

Chaos Testing

Netflix chaos engineering patterns for testing application resilience. Inject random errors and latency to verify that circuit breakers, fallback chains, and error boundaries work under stress.

Safety: All chaos functions are disabled by default (enabled: false). You must explicitly enable them. Never enable chaos in production.

Install from registry

npx shadcn@latest add https://registry.mukoko.com/api/v1/ui/chaos

Live demonstration

Chaos injection combined with circuit breaker. Adjust the error rate and latency, then send requests to see how the system responds under stress.

Circuit: CLOSEDError rate: 30%Latency: 100-500ms

Adjust chaos parameters and send requests to see resilience in action. The circuit breaker will open after 3 chaos-injected failures.

Chaos injection + circuit breaker working together. Crank up the error rate to see the circuit open, then watch it recover through HALF_OPEN after 5 seconds.

withChaos

Wrap any async operation with configurable error and latency injection. Passes through unchanged when disabled.

chaos testing
import { withChaos, ChaosError } from "@/lib/chaos" // Only in development/testing const data = await withChaos( () => fetch("/api/weather").then(r => r.json()), { enabled: process.env.NODE_ENV === "development", errorRate: 0.3, // 30% chance of failure latencyMs: [100, 500], // 100-500ms random delay } ) // Distinguish chaos errors from real errors try { await withChaos(() => fetchData(), { enabled: true, errorRate: 0.5 }) } catch (error) { if (error instanceof ChaosError) { console.log("Chaos injected:", error.chaosType) // "error" | "latency" console.log(error.injected) // true } }

chaosMiddleware

Create a reusable chaos wrapper with fixed configuration.

middleware pattern
import { chaosMiddleware } from "@/lib/chaos" const devChaos = chaosMiddleware({ enabled: process.env.CHAOS_TESTING === "true", errorRate: 0.2, latencyMs: [50, 200], }) // Wrap any operation const weather = await devChaos(() => fetchWeather(slug)) const user = await devChaos(() => getUser(id)) const report = await devChaos(() => submitReport(data))

Combined with circuit breaker

The real power is testing your resilience stack end-to-end: chaos injects failures, the circuit breaker opens, the fallback chain kicks in.

end-to-end resilience testing
import { withChaos } from "@/lib/chaos" import { CircuitBreaker, PROVIDER_CONFIGS } from "@/lib/circuit-breaker" import { withFallback } from "@/lib/fallback-chain" import { withRetry } from "@/lib/retry" const breaker = new CircuitBreaker({ name: "weather-api", ...PROVIDER_CONFIGS["tomorrow-io"], }) // Chaos wraps the innermost call const weather = await withFallback([ { name: "primary", execute: () => withRetry( () => breaker.execute(() => withChaos(() => fetchPrimary(), { enabled: true, errorRate: 0.5, // 50% failure rate }) ), { maxAttempts: 2 } ), timeoutMs: 10000, }, { name: "cache", execute: () => getFromCache(), }, ]) // Verify: // 1. Chaos injects failures in primary // 2. Retry attempts 2x before giving up // 3. Circuit breaker opens after 3 total failures // 4. Fallback chain falls through to cache

Testing patterns

  • Start with low error rates (10-20%) and increase gradually
  • Test each layer independently first, then the full stack
  • Verify circuit breaker opens at the expected failure threshold
  • Confirm fallback chain reaches the last stage under heavy chaos
  • Check that error boundaries show appropriate fallback UI
  • Monitor memory with useMemoryPressure during chaos testing
  • Never enable chaos in production — use environment variables
Last updated on