如何用pytest测试指定装饰器是否已应用到多个函数上
实现方案
你已经单独完成了装饰器本身的功能测试,现在只需要做绑定校验,不需要为每个端点重复写功能测试,以下是最简便的实现方式:
方案1:给装饰器加专属标识(优先推荐)
首先给你的check_user装饰器加一个自定义属性作为绑定标记,性能最高、逻辑最稳定:
from functools import wraps def check_user(func): """Only allow admins to change the user_id in the annotated function. Use as decorator: @check_user """ @wraps(func) def wrapper(*args, **kwargs): # 原有装饰器逻辑不变 ... # 新增专属标记属性 wrapper.__check_user_applied = True return wrapper
然后编写参数化测试,直接导入所有需要校验的FastAPI端点函数,判断标记是否存在即可:
import pytest # 导入你所有加了check_user装饰器的端点函数 from your_api_module import foo, bar, gaga, xxx, yyy @pytest.mark.parametrize("endpoint_func", [ foo, bar, gaga, xxx, yyy # 把20个端点都列在这里 ]) def test_check_user_decorator_bound(endpoint_func): assert hasattr(endpoint_func, "__check_user_applied"), f"端点{endpoint_func.__name__}未绑定check_user装饰器" assert endpoint_func.__check_user_applied is True
如果存在多层装饰器叠加的情况,可以加一个递归校验的辅助函数,逐层查找被包装的函数:
def has_check_user_decorator(func): current_func = func while current_func: if hasattr(current_func, "__check_user_applied") and current_func.__check_user_applied: return True # 拿到上一层被包装的原函数 current_func = getattr(current_func, "__wrapped__", None) return False # 测试用例改成调用辅助函数 @pytest.mark.parametrize("endpoint_func", [foo, bar, gaga,]) def test_check_user_decorator_bound(endpoint_func): assert has_check_user_decorator(endpoint_func), f"端点{endpoint_func.__name__}未绑定check_user装饰器"
进阶优化:自动扫描端点无需手动罗列
如果不想每次新增端点都手动更新测试的参数列表,可以直接扫描FastAPI实例的路由自动获取所有端点函数:
import pytest from your_project.main import app # 导入你的FastAPI实例对象 def get_target_endpoints(): endpoints = [] for route in app.routes: if not hasattr(route, 'endpoint'): continue # 可在这里加过滤逻辑,比如只校验路径前缀为/admin的端点 if route.path.startswith("/admin"): endpoints.append(route.endpoint) return endpoints @pytest.mark.parametrize("endpoint_func", get_target_endpoints()) def test_check_user_decorator_bound(endpoint_func): assert has_check_user_decorator(endpoint_func), f"端点{endpoint_func.__name__}未绑定check_user装饰器"
方案2:无需修改装饰器的校验方式
如果不方便修改原有装饰器的代码,可以用inspect模块检查闭包信息判断装饰器是否被应用,稳定性略低于加标记的方案:
import inspect from your_module import check_user def is_decorated_with_check_user(func): current_func = func while current_func: closure_vars = inspect.getclosurevars(current_func) if closure_vars.globals.get('check_user') is check_user: return True current_func = getattr(current_func, "__wrapped__", None) return False
测试用例的逻辑和方案1一致,直接调用这个判断函数即可。
内容的提问来源于stack exchange,提问作者HeyMan
相关产品推荐
相关产品推荐

