Chaos Engineering
agnt includes a built-in chaos engineering system for testing how your frontend handles network failures, slow connections, and unreliable APIs. Test the unhappy paths without changing your code.
Why Chaos Testing for Frontend?
Most frontend bugs happen under conditions developers never test:
- Slow networks - 3G connections, hotel WiFi, subway tunnels
- Flaky APIs - 500 errors, timeouts, rate limits
- Race conditions - Responses arriving out of order
- Stale tabs - Browser tabs open for hours with expired tokens
agnt lets you simulate all of these with a single command.
Quick Start
# Start proxy with chaos preset
proxy {action: "start", id: "app", target_url: "http://localhost:3000"}
proxy {action: "chaos", id: "app", preset: "flaky-api"}
Your app now experiences random 500 errors, timeouts, and variable latency. Watch what breaks.
Built-in Presets
| Preset | What It Simulates | Use For |
|---|---|---|
mobile-3g | 200-2000ms latency, 2% packet loss | Mobile network testing |
mobile-4g | 50-500ms latency, 0.5% packet loss | LTE network testing |
flaky-api | Random 500s, timeouts, variable latency | API resilience testing |
race-condition | Out-of-order responses, high variance delays | Race condition bugs |
stale-tab | 3-hour delays | Token expiry, stale state |
slow-connection | 5KB/s bandwidth throttling | Slow network handling |
connection-drops | 10% mid-response disconnects | Retry logic testing |
data-corruption | 5% truncated responses | Partial data handling |
rate-limited | 20% 429 errors | Rate limit UI testing |
auth-failures | 10% 401/403 errors | Auth error handling |
service-degradation | Mixed latency + errors + truncation | Graceful degradation |
pressure-test | Everything at once | Stress testing |
Preset Examples
Test mobile network conditions:
proxy {action: "chaos", id: "app", preset: "mobile-3g"}
# Your app now has 200-2000ms latency and 2% packet loss
Expose race conditions:
proxy {action: "chaos", id: "app", preset: "race-condition"}
# API responses arrive out of order - does your state get corrupted?
Test stale browser tabs:
proxy {action: "chaos", id: "app", preset: "stale-tab"}
# Simulates a tab that's been open for hours - test token refresh
Custom Chaos Rules
For fine-grained control, define custom rules:
proxy {
action: "chaos",
id: "app",
rules: [
{
"id": "api-latency",
"name": "Slow API calls",
"type": "latency",
"enabled": true,
"url_pattern": "/api/.*",
"min_latency_ms": 500,
"max_latency_ms": 3000,
"probability": 0.5
},
{
"id": "checkout-errors",
"name": "Checkout failures",
"type": "http_error",
"enabled": true,
"url_pattern": "/api/checkout",
"error_codes": [500, 503],
"probability": 0.1
}
]
}
Chaos Types
Network Chaos
| Type | Description | Configuration |
|---|---|---|
latency | Add delays to responses | min_latency_ms, max_latency_ms, jitter_ms |
bandwidth | Limit data transfer rate | (use slow_drip instead) |
packet_loss | Drop random requests entirely | probability |
disconnect | Drop connection mid-response | drop_after_percent, drop_after_bytes |
slow_drip | Trickle bytes slowly | bytes_per_ms, chunk_size |
timeout | Never respond (simulate timeout) | probability |
Response Timing
| Type | Description | Configuration |
|---|---|---|
out_of_order | Deliver responses in random order | reorder_min_requests, reorder_max_wait_ms |
stale | Very long delays (hours) | stale_delay_ms |
HTTP Errors
| Type | Description | Configuration |
|---|---|---|
http_error | Inject HTTP error codes | error_codes[], error_message |
rate_limit | Simulate 429 rate limiting | (use http_error with code 429) |
Data Corruption
| Type | Description | Configuration |
|---|---|---|
truncate | Cut off response body | truncate_percent (0.0-1.0, portion to keep) |
bit_flip | Random byte changes | (advanced) |
corrupt_json | Malform JSON responses | (advanced) |
Rule Configuration
Matching Criteria
{
"url_pattern": "/api/users.*", // Regex pattern for URLs
"methods": ["POST", "PUT"], // HTTP methods (empty = all)
"probability": 0.3 // 0.0-1.0, chance of applying
}
Latency Configuration
{
"type": "latency",
"min_latency_ms": 100, // Minimum delay
"max_latency_ms": 2000, // Maximum delay
"jitter_ms": 500 // Random +/- jitter
}
Slow-Drip (Bandwidth Throttling)
{
"type": "slow_drip",
"bytes_per_ms": 5, // 5 bytes/ms = 5KB/s
"chunk_size": 10 // Write 10 bytes at a time
}
Connection Drops
{
"type": "disconnect",
"drop_after_percent": 0.5, // Drop after 50% of body sent
// OR
"drop_after_bytes": 1024 // Drop after 1KB sent
}
Response Reordering
{
"type": "out_of_order",
"reorder_min_requests": 3, // Batch this many before shuffling
"reorder_max_wait_ms": 500 // Max time to wait for batch
}
Managing Chaos
Check Status
proxy {action: "chaos", id: "app", status: true}
Returns:
{
"enabled": true,
"preset": "flaky-api",
"stats": {
"total_requests": 142,
"affected_count": 38,
"latency_injected_ms": 45000,
"errors_injected": 7,
"drops_injected": 2
}
}
Disable Chaos
proxy {action: "chaos", id: "app", enabled: false}
Clear All Rules
proxy {action: "chaos", id: "app", clear: true}
Enable/Disable Specific Rules
proxy {action: "chaos", id: "app", enable_rule: "api-latency"}
proxy {action: "chaos", id: "app", disable_rule: "checkout-errors"}
Real-World Testing Scenarios
Testing Loading States
# Add 2-5 second delay to all API calls
proxy {
action: "chaos",
id: "app",
rules: [{
"type": "latency",
"min_latency_ms": 2000,
"max_latency_ms": 5000,
"probability": 1.0
}]
}
Now verify: Do your loading spinners appear? Is the UI responsive during loads?
Testing Error Handling
# 50% of API calls fail with server errors
proxy {
action: "chaos",
id: "app",
rules: [{
"type": "http_error",
"error_codes": [500, 502, 503],
"probability": 0.5
}]
}
Now verify: Do error messages appear? Can users retry? Does state stay consistent?
Testing Race Conditions
# Responses arrive in random order
proxy {action: "chaos", id: "app", preset: "race-condition"}
Now verify: If user types in search and gets responses out of order, does the UI show stale results?
Testing Offline Behavior
# Drop 100% of requests
proxy {
action: "chaos",
id: "app",
rules: [{
"type": "packet_loss",
"probability": 1.0
}]
}
Now verify: Does offline detection trigger? Are there helpful error messages?
Testing Token Expiry
# Return 401 for all API calls
proxy {
action: "chaos",
id: "app",
rules: [{
"type": "http_error",
"url_pattern": "/api/.*",
"error_codes": [401],
"probability": 1.0
}]
}
Now verify: Does the app redirect to login? Is state preserved for after re-auth?
Reproducible Chaos
For consistent test runs, set a random seed:
proxy {
action: "chaos",
id: "app",
seed: 12345,
rules: [...]
}
Same seed = same sequence of chaos decisions.
Integration with Testing
Manual Exploratory Testing
- Start your dev server and proxy
- Apply a chaos preset
- Click through your app
- Watch for broken states, missing loaders, poor error messages
Automated E2E Tests
// In Playwright/Cypress test
beforeEach(async () => {
// Enable chaos via agnt API
await fetch('http://localhost:45849/__chaos', {
method: 'POST',
body: JSON.stringify({ preset: 'flaky-api' })
});
});
test('handles API errors gracefully', async ({ page }) => {
await page.goto('/dashboard');
// Error states should be visible due to chaos
await expect(page.locator('.error-message')).toBeVisible();
await expect(page.locator('.retry-button')).toBeEnabled();
});
Best Practices
- Start with presets - They cover common scenarios well
- Test one thing at a time - Don't combine too many chaos types
- Document expected behavior - What SHOULD happen under chaos?
- Add chaos to CI - Run a subset of E2E tests with chaos enabled
- Use probability < 1.0 - Intermittent failures are harder to handle than consistent ones
See Also
- Reverse Proxy - Proxy setup and configuration
- Mobile Testing - Testing on real devices
- Automated Testing - CI/CD integration