Master Decorator Factories: Logging, Retry, Permissions & More for API Testing

This article explores how decorator factories can enhance API automation testing by providing reusable solutions for logging, automatic retries, permission checks, performance monitoring, data-driven testing, and environment switching, complete with Python code examples and usage demonstrations.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
Master Decorator Factories: Logging, Retry, Permissions & More for API Testing

Decorator factories are widely used in API automation testing frameworks to improve code readability, reusability, and to implement cross‑cutting concerns such as logging, retries, permission checks, performance monitoring, data‑driven testing, and environment switching.

1. Logging decorator factory (supports different levels)

Creates a decorator that logs API calls at the specified level (info or debug).

def make_logger(level='info', enabled=True):
    def logger_decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if not enabled:
                return func(*args, **kwargs)
            log_method = {'info': logging.info, 'debug': logging.debug}.get(level.lower(), logging.info)
            log_method(f"[{func.__name__}] 正在调用接口...")
            result = func(*args, **kwargs)
            log_method(f"[{func.__name__}] 接口调用完成")
            return result
        return wrapper
    return logger_decorator

Usage example:

@make_logger('debug')
def get_user_info(user_id):
    print(f"获取用户 {user_id} 的信息")

get_user_info(1001)

Sample output demonstrates debug logs before and after the function execution.

2. Automatic retry decorator factory

Encapsulates a generic retry logic that automatically retries a failing operation up to N times, suitable for unstable network requests.

def retry(max_retries=3, delay=1, exceptions=(Exception,)):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            retries = 0
            while retries <= max_retries:
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    retries += 1
                    logging.warning(f"[{func.__name__}] 第 {retries}/{max_retries} 次重试失败: {e}")
                    if retries > max_retries:
                        raise
                    time.sleep(delay)
            return None
        return wrapper
    return decorator

Usage example:

@retry(max_retries=3, exceptions=(requests.exceptions.RequestException,))
def fetch_data(url):
    response = requests.get(url)
    return response.json()

try:
    data = fetch_data("https://api.example.com/data")
    print(data)
except Exception as e:
    print("最终请求失败:", e)

3. Permission verification decorator factory (supports role control)

Determines whether a user with a specific role (e.g., admin, user) is allowed to execute an API function.

def permission_required(role='user'):
    def decorator(func):
        @wraps(func)
        def wrapper(context, *args, **kwargs):
            user_role = context.get('role')
            if user_role != role:
                logging.error(f"[{func.__name__}] 权限不足,需要 {role} 角色,当前为 {user_role}")
                raise PermissionError(f"权限不足,需要 {role} 角色")
            return func(context, *args, **kwargs)
        return wrapper
    return decorator

Usage example:

@permission_required('admin')
def delete_user(context, user_id):
    print(f"管理员 {context['username']} 删除了用户 {user_id}")

context = {'username': '张三', 'role': 'admin'}
delete_user(context, 1002)

4. Performance monitoring decorator factory

Records the execution time of an API function for performance analysis or optimization.

import time

def performance_monitor(enabled=True):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            if not enabled:
                return func(*args, **kwargs)
            start = time.time()
            result = func(*args, **kwargs)
            duration = time.time() - start
            logging.info(f"[{func.__name__}] 执行耗时: {duration:.4f} 秒")
            return result
        return wrapper
    return decorator

Usage example:

@performance_monitor()
def slow_api():
    time.sleep(1.2)

slow_api()

5. Data‑driven decorator factory (parameterized testing)

Dynamically injects test data into an API function to achieve parameterized testing.

def data_provider(data_list):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for data in data_list:
                logging.info(f"运行测试用例: {data}")
                func(*data)
            return wrapper
    return decorator

Usage example:

@test_data([
    ("张三", "北京"),
    ("李四", "上海"),
    ("王五", "广州")
])
def test_user_location(name, city):
    print(f"{name} 的城市是 {city}")

test_user_location()

6. Environment switch decorator factory (dev/test/prod)

Automatically switches API endpoints or behavior based on the configured environment.

def environment_switch(env='dev'):
    base_urls = {
        'dev': 'http://dev.api.example.com',
        'test': 'http://test.api.example.com',
        'prod': 'http://api.example.com'
    }
    def decorator(func):
        @wraps(func)
        def wrapper(endpoint, *args, **kwargs):
            full_url = f"{base_urls[env]}/{endpoint}"
            logging.info(f"[{func.__name__}] 请求地址: {full_url}")
            return func(full_url, *args, **kwargs)
        return wrapper
    return decorator

Usage example:

@environment_switch('test')
def call_api(url):
    print(f"正在调用接口: {url}")

call_api("users/list")

Sample output shows the constructed request URL and the call message.

Summary table comparing the six decorator factories:

pythonautomationbackend developmentDecoratorAPI testing
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.