How to Inject Random Latency for Chaos Testing in Python
Mock unreliable services by wrapping functions with a decorator that adds random network-like delays before execution.
Python code
23 linesimport random
import time
from functools import wraps
def inject_latency(func):
@wraps(func)
def wrapper(*args, **kwargs):
latency = random.uniform(0.1, 0.5)
print(f"Injecting {latency:.3f}s latency...")
time.sleep(latency)
return func(*args, **kwargs)
return wrapper
@inject_latency
def fetch_data():
return {"status": "ok", "data": [1, 2, 3]}
if __name__ == "__main__":
for _ in range(3):
start = time.time()
result = fetch_data()
elapsed = time.time() - start
print(f"Result: {result}, elapsed: {elapsed:.3f}s")
Output
Injecting 0.123s latency...
Result: {'status': 'ok', 'data': [1, 2, 3]}, elapsed: 0.123s
Injecting 0.456s latency...
Result: {'status': 'ok', 'data': [1, 2, 3]}, elapsed: 0.456s
Injecting 0.089s latency...
Result: {'status': 'ok', 'data': [1, 2, 3]}, elapsed: 0.089s
How it works
The inject_latency decorator uses functools.wraps to preserve the original function's metadata, which matters for debugging and introspection. Each call draws a fresh random value with random.uniform, simulating unpredictable network conditions. Adding time.sleep inside the wrapper introduces the artificial delay before the real function runs. This pattern lets you test how your system copes with slow downstream dependencies without touching the target service. Because the decorator wraps any callable, you can apply it selectively in test and staging environments to observe real-world timing behavior.
Common mistakes
- Applying the decorator in production code and forgetting to remove it before release
- Using `random.randint` instead of `uniform` to miss the fractional delays real networks show
- Sleeping after the function call instead of before, which measures the wrong timing profile
Variations
- Parameterize the decorator so the delay range, failure rate, or error injection are configurable from environment variables
Real-world use cases
- Testing that a payment gateway client has sane timeouts and retries when the upstream API randomly slows down.
- Verifying that a microservice's circuit breaker trips correctly during a chaos engineering drill.
- Simulating flaky third-party webhooks in a staging environment to harden queue processing jobs.
Sponsored
More from Reliability & rate limiting
- At Least Once with Idempotent Consumer in Python medium
- Build a Rate Limiter Decorator in Python easy
- Build a queue-based admission control system in Python easy
- Chaos Inject Random Failures in Python easy
- Circuit breaker failure threshold count in Python medium
- Exactly Once Processing Dedupe Mock in Python easy
Keep learning
Related tutorials and quizzes for this topic.