37 / 80 · 12 Programming for QA · Functional Patterns for Testing← prev⊞ allnext →☰ Read as one page
5.3Closures
A closure captures variables from its enclosing scope — useful for factory functions that create pre-configured utilities.
API Client Factory
def make_api_client(base_url: str, token: str):
"""Creates a pre-configured API client."""
def request(method: str, path: str, **kwargs):
headers = {"Authorization": f"Bearer {token}"}
headers.update(kwargs.pop("headers", {}))
return requests.request(method, f"{base_url}{path}", headers=headers, **kwargs)
return request
# Usage: create clients for different environments
staging_api = make_api_client("https://api.staging.example.com", os.environ["STAGING_TOKEN"])
prod_api = make_api_client("https://api.example.com", os.environ["PROD_TOKEN"])
# Both use the same interface
staging_response = staging_api("GET", "/users/me")
prod_response = prod_api("GET", "/users/me")
Retry Wrapper
def with_retry(max_attempts: int = 3, delay: float = 1.0):
"""Creates a retry wrapper for flaky operations."""
def decorator(func):
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
if attempt < max_attempts - 1:
time.sleep(delay * (attempt + 1))
raise last_exception
return wrapper
return decorator
@with_retry(max_attempts=3, delay=0.5)
def fetch_user(user_id: str):
response = requests.get(f"{base_url}/users/{user_id}")
response.raise_for_status()
return response.json()
TypeScript Closures
function makeApiClient(baseUrl: string, token: string) {
return async (method: string, path: string, body?: object) => {
const response = await fetch(`${baseUrl}${path}`, {
method,
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
return response;
};
}
const api = makeApiClient("https://api.staging.example.com", process.env.TOKEN!);
const response = await api("GET", "/users/me");