Pytest 对测试发现有严格的命名要求:
- 文件名必须是
test_*.py或*_test.py - 函数名必须以
test_开头
用户希望使用 @evaluation_test 装饰器后,无论如何命名都能被发现。
使用 @evaluation_test 装饰的函数会自动注册正确的测试名称。
# 任何函数名都可以!
@evaluation_test(...)
async def my_custom_eval(row: EvaluationRow) -> EvaluationRow:
# 自动注册为 test_my_custom_eval
...方式 1:明确指定文件(最简单)
pytest path/to/any_filename.py方式 2:使用标准命名
# 文件名: test_*.py
pytest # 自动发现方式 3:使用 --ep-discover-all 标志
pytest --ep-discover-all # 发现所有 .py 文件中的测试文件: eval_protocol/pytest/evaluation_test.py
# 在 decorator 返回之前自动注册
original_name = test_func.__name__
if not original_name.startswith('test_'):
import sys
frame = sys._getframe(1)
caller_globals = frame.f_globals
test_name = f'test_{original_name}'
if test_name not in caller_globals:
caller_globals[test_name] = dual_mode_wrapper工作原理:
- 使用
sys._getframe(1)获取调用者的全局命名空间 - 在命名空间中注册
test_{function_name}别名 - Pytest 扫描模块时发现这个别名
文件: eval_protocol/pytest/parameterize.py
# 确保 wrapper 的 __name__ 以 test_ 开头
original_name = test_func.__name__
if not original_name.startswith('test_'):
wrapper.__name__ = f'test_{original_name}'文件: eval_protocol/pytest/dual_mode_wrapper.py
# 确保 dual_mode_wrapper 的 __name__ 以 test_ 开头
original_name = test_func.__name__
if not original_name.startswith('test_'):
dual_mode_wrapper.__name__ = f'test_{original_name}'文件: eval_protocol/pytest/plugin.py
def pytest_addoption(parser) -> None:
group = parser.getgroup("eval-protocol")
group.addoption(
"--ep-discover-all",
action="store_true",
default=False,
help=(
"Discover @evaluation_test in all Python files, "
"not just test_*.py files."
),
)
def pytest_configure(config) -> None:
# 启用发现所有 .py 文件
if config.getoption("--ep-discover-all", default=False):
config.option.python_files = ["*.py"]tests/test_auto_discovery_simple.py- 验证函数名自动注册examples/auto_discovery_example.py- 标准命名示例examples/my_evaluation.py- 非标准命名示例
# 1. 非标准文件名 + 非标准函数名
$ pytest examples/my_evaluation.py --collect-only -v
collected 1 item
<Coroutine test_custom_evaluation[rows(len=1)]> ✅
# 2. 运行测试
$ pytest examples/my_evaluation.py -v
============================== 1 passed in 0.08s =============================== ✅
# 3. 标准命名
$ pytest examples/auto_discovery_example.py --collect-only -v
collected 3 items
<Coroutine test_math_evaluation[rows(len=1)]> ✅
<Coroutine test_greeting_evaluation[rows(len=1)]> ✅
<Coroutine test_coding_task_evaluation[rows(len=1)]> ✅# 文件: evals/math.py
from eval_protocol.pytest import evaluation_test
from eval_protocol.models import EvaluationRow, EvaluateResult
@evaluation_test(
input_rows=[[EvaluationRow(messages=[{"role": "user", "content": "2+2"}])]]
)
async def evaluate_addition(row: EvaluationRow) -> EvaluationRow:
row.evaluation_result = EvaluateResult(score=1.0)
return row运行:
pytest evals/math.py -v# 文件: tests/test_math_eval.py
@evaluation_test(...)
async def test_addition(row: EvaluationRow) -> EvaluationRow:
...运行:
pytest tests/ # 自动发现所有 test_*.py# 文件: test_my_evals.py(标准文件名)
@evaluation_test(...)
async def math_accuracy_check(row: EvaluationRow) -> EvaluationRow:
# 函数名不标准也没问题
...运行:
pytest # 自动发现| 特性 | 状态 | 说明 |
|---|---|---|
| 函数名自由 | ✅ | 任何函数名都能被发现 |
| 文件名灵活 | ✅ | 支持明确指定或使用标志 |
| 零配置 | ✅ | 函数名完全自动处理 |
| 向后兼容 | ✅ | 不影响现有代码 |
| 无警告 | ✅ | 静默自动处理 |
development/auto_test_discovery.md- 技术实现细节development/file_and_function_naming.md- 文件名和函数名处理指南development/FINAL_SUMMARY.md- 功能总结development/COMPLETE_SOLUTION.md- 本文档
pytest path/to/your_file.py- ✅ 任何文件名都可以
- ✅ 任何函数名都可以
- ✅ 无需额外配置
# 文件: test_*.py
# 函数: test_* 或任意名称
pytest- ✅ 自动发现
- ✅ 团队熟悉的方式
pytest --ep-discover-all- ✅ 发现所有文件中的测试
- ✅ 适合大量非标准命名文件
现在使用 @evaluation_test 装饰器:
- 函数名:完全自由,自动处理 ✅
- 文件名:
- 明确指定:
pytest your_file.py✅ - 标准命名:
test_*.py自动发现 ✅ - 或使用:
pytest --ep-discover-all✅
- 明确指定:
用户只需要使用 @evaluation_test,其他都自动完成! 🎉