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.

Easy Python 3.9+ Aug 9, 2026 Reliability & rate limiting 13 views 0 copies

Python code

23 lines
Python 3.9+
import 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

stdout
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

  1. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Reliability & rate limiting

Related tutorials and quizzes for this topic.