MAUI Chaos Engineering
C# MAUI Chaos Engineering Fault Injection Network Simulation UI Resilience Error Handling Polly Retry Circuit Breaker Production
| Chaos Scenario | Simulation | Expected Behavior | Test On |
|---|---|---|---|
| Network Timeout | Delay response 30+ sec | Timeout message, retry button | All platforms |
| Server Error (500) | Return HTTP 500 | Error message, auto retry | All platforms |
| Auth Expired (401) | Return HTTP 401 | Redirect to login, refresh token | All platforms |
| Offline | No internet | Offline banner, cached data | Android, iOS |
| Slow Network | 3-5 sec delay | Loading indicator, no freeze | All platforms |
| Memory Pressure | Large data load | No crash, graceful degradation | Android, iOS |
Fault Injection Setup
# === C# MAUI Chaos Engineering Setup ===
# // IChaosService.cs — Interface for chaos injection
# public interface IApiService
# {
# Task<List<Item>> GetItemsAsync();
# Task<Item> GetItemAsync(int id);
# Task<bool> SaveItemAsync(Item item);
# }
#
# // ChaosApiService.cs — Chaos implementation
# public class ChaosApiService : IApiService
# {
# private readonly IApiService _realService;
# private readonly Random _random = new();
# private readonly double _failureRate;
#
# public ChaosApiService(IApiService realService, double failureRate = 0.3)
# {
# _realService = realService;
# _failureRate = failureRate;
# }
#
# public async Task<List<Item>> GetItemsAsync()
# {
# await SimulateChaos();
# return await _realService.GetItemsAsync();
# }
#
# private async Task SimulateChaos()
# {
# if (_random.NextDouble() < _failureRate)
# {
# var scenario = _random.Next(4);
# switch (scenario)
# {
# case 0: throw new HttpRequestException("Network error");
# case 1: throw new TimeoutException("Request timeout");
# case 2: await Task.Delay(5000); break; // Slow response
# case 3: throw new UnauthorizedAccessException("Token expired");
# }
# }
# }
# }
#
# // MauiProgram.cs — DI Registration
# #if DEBUG
# builder.Services.AddSingleton<IApiService>(sp =>
# new ChaosApiService(new RealApiService(sp.GetRequiredService<HttpClient>()), 0.3));
# #else
# builder.Services.AddSingleton<IApiService, RealApiService>();
# #endif
from dataclasses import dataclass
@dataclass
class ChaosScenario:
scenario: str
exception: str
frequency: str
user_impact: str
handling: str
scenarios = [
ChaosScenario("Network Error",
"HttpRequestException", "30% of requests",
"Cannot load data, shows error",
"Retry 3x with backoff, show cached data"),
ChaosScenario("Timeout",
"TimeoutException / TaskCanceledException", "20% of requests",
"Long wait, then error",
"30s timeout, cancel task, show retry button"),
ChaosScenario("Auth Expired",
"UnauthorizedAccessException / HTTP 401", "10% of requests",
"Redirected to login",
"Refresh token automatically, retry once"),
ChaosScenario("Server Error",
"HttpRequestException (500)", "15% of requests",
"Feature unavailable",
"Circuit breaker, show maintenance message"),
ChaosScenario("Slow Response",
"Task.Delay(3000-5000)", "25% of requests",
"Loading feels slow",
"Show skeleton UI, progress indicator"),
]
print("=== Chaos Scenarios ===")
for s in scenarios:
print(f" [{s.scenario}] Exception: {s.exception}")
print(f" Frequency: {s.frequency} | Impact: {s.user_impact}")
print(f" Handling: {s.handling}")
Polly Resilience
# === Polly Configuration ===
# // Polly setup in MauiProgram.cs
# builder.Services.AddHttpClient("api", client =>
# {
# client.BaseAddress = new Uri("https://api.example.com");
# client.Timeout = TimeSpan.FromSeconds(30);
# })
# .AddPolicyHandler(GetRetryPolicy())
# .AddPolicyHandler(GetCircuitBreakerPolicy())
# .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(10));
#
# static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
# {
# return HttpPolicyExtensions
# .HandleTransientHttpError()
# .Or<TimeoutRejectedException>()
# .WaitAndRetryAsync(3, retryAttempt =>
# TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
# onRetry: (outcome, timespan, retryAttempt, context) =>
# {
# Debug.WriteLine($"Retry {retryAttempt} after {timespan.TotalSeconds}s");
# });
# }
#
# static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
# {
# return HttpPolicyExtensions
# .HandleTransientHttpError()
# .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30),
# onBreak: (result, duration) =>
# Debug.WriteLine($"Circuit OPEN for {duration.TotalSeconds}s"),
# onReset: () => Debug.WriteLine("Circuit CLOSED"));
# }
@dataclass
class PollyPolicy:
policy: str
config: str
when: str
behavior: str
policies = [
PollyPolicy("Retry",
"3 retries, exponential backoff (2s, 4s, 8s)",
"Transient HTTP errors (5xx, timeout)",
"Auto retry, user sees loading longer"),
PollyPolicy("Circuit Breaker",
"Open after 5 failures, 30s cooldown",
"Consecutive failures to same endpoint",
"Fast fail, no wasted requests, show fallback"),
PollyPolicy("Timeout",
"10s per request, 30s overall",
"Slow API responses",
"Cancel request, show timeout message"),
PollyPolicy("Fallback",
"Return cached data on failure",
"After all retries exhausted",
"Show stale data with 'offline' indicator"),
PollyPolicy("Bulkhead",
"Max 10 concurrent requests",
"Prevent resource exhaustion",
"Queue excess requests, prevent app freeze"),
]
print("=== Polly Policies ===")
for p in policies:
print(f" [{p.policy}] Config: {p.config}")
print(f" When: {p.when}")
print(f" Behavior: {p.behavior}")
UI Resilience Testing
# === UI Testing Checklist ===
@dataclass
class UITest:
test: str
scenario: str
expected: str
platform: str
automated: bool
ui_tests = [
UITest("Loading State", "API call in progress",
"Skeleton UI or spinner visible, no blank screen",
"All", True),
UITest("Error State", "API returns 500",
"Error message with retry button, no crash",
"All", True),
UITest("Offline Banner", "No internet connection",
"Banner shows 'Offline', cached data displayed",
"Android, iOS", True),
UITest("Timeout Recovery", "API timeout then succeeds",
"Shows timeout, auto retry succeeds, UI updates",
"All", True),
UITest("Auth Redirect", "Token expired (401)",
"Redirect to login, after login return to same page",
"All", True),
UITest("Pull to Refresh", "Stale data after error",
"Pull down refreshes data, loading indicator shows",
"Android, iOS", True),
UITest("Back Navigation", "Error on current page",
"Back button works, no stuck state",
"All", True),
UITest("Memory Pressure", "Load 10000 items in list",
"CollectionView virtualizes, no OOM crash",
"Android, iOS", False),
]
print("=== UI Resilience Tests ===")
for t in ui_tests:
auto = "Automated" if t.automated else "Manual"
print(f" [{t.test}] {auto} | Platform: {t.platform}")
print(f" Scenario: {t.scenario}")
print(f" Expected: {t.expected}")
เคล็ดลับ
- Polly: ใช้ Polly ทุก HTTP Call ไม่ยกเว้น Retry + Circuit Breaker + Timeout
- Offline: มี Offline Mode เก็บ Cache ไว้ใช้เมื่อไม่มี Internet
- UI: ทุกหน้าต้องมี Loading State Error State Empty State
- Test: ทดสอบ Chaos บนทุก Platform Android iOS Windows
- Monitor: ใช้ AppCenter หรือ Sentry เก็บ Crash Report จาก Production
MAUI Chaos Engineering คืออะไร
ทดสอบความทนทาน .NET MAUI Network หลุด API Timeout Error Memory Leak Android iOS Windows UI ไม่ค้าง Crash Error Message Retry Offline
Fault Injection ทำอย่างไร
Interface IApiService DI Chaos Service Exception Timeout Slow Response สุ่ม Failure Polly Retry Circuit Breaker Feature Flag Scenario
Network Simulation ทำอย่างไร
HttpMessageHandler Timeout 500 Error 401 Unauthorized Slow Network Offline Charles Proxy mitmproxy Device จริง WiFi ปิด
Best Practices มีอะไร
Polly Retry 3 Exponential Backoff Circuit Breaker Timeout 30s Offline Cache Loading Error Message Global Exception Handler ทุก Platform
สรุป
C# MAUI Chaos Engineering Fault Injection Network Simulation Polly Retry Circuit Breaker UI Resilience Offline Cache Error Handling Production
