Files
demo2-qwen/app/test_runner.py

81 lines
2.7 KiB
Python
Raw Normal View History

2026-03-10 10:35:03 +08:00
"""
app/test_runner.py - 在临时目录中真实执行 pytest 单元测试
"""
import os
import re
import sys
import tempfile
import subprocess
def run_python_tests(python_code: str, test_code: str) -> dict:
"""
将业务代码写入 implementation.py测试代码写入 test_implementation.py
在隔离的临时目录中用 pytest 执行返回结构化测试结果
Returns:
dict: {
success: bool,
passed: int,
failed: int,
errors: int,
total: int,
output: str # pytest 完整输出
}
"""
with tempfile.TemporaryDirectory() as tmpdir:
impl_path = os.path.join(tmpdir, "implementation.py")
test_path = os.path.join(tmpdir, "test_implementation.py")
conftest_path = os.path.join(tmpdir, "conftest.py")
with open(impl_path, "w", encoding="utf-8") as f:
f.write(python_code)
with open(test_path, "w", encoding="utf-8") as f:
f.write(test_code)
# conftest.py 确保 tmpdir 在 sys.path 首位,解决模块导入问题
with open(conftest_path, "w", encoding="utf-8") as f:
f.write("import sys, os\nsys.path.insert(0, os.path.dirname(__file__))\n")
try:
proc = subprocess.run(
[sys.executable, "-m", "pytest", "test_implementation.py",
"-v", "--tb=short", "--no-header", "--color=no"],
cwd=tmpdir,
capture_output=True,
text=True,
timeout=60,
env={**os.environ, "PYTHONPATH": tmpdir},
)
output = proc.stdout
if proc.stderr.strip():
output += "\n--- stderr ---\n" + proc.stderr
except subprocess.TimeoutExpired:
return {
"success": False,
"passed": 0, "failed": 0, "errors": 1, "total": 0,
"output": "❌ 测试执行超时(超过 60 秒)",
}
except FileNotFoundError:
return {
"success": False,
"passed": 0, "failed": 0, "errors": 1, "total": 0,
"output": "❌ 未找到 pytest请确保已安装pip install pytest",
}
passed = int(m.group(1)) if (m := re.search(r"(\d+) passed", output)) else 0
failed = int(m.group(1)) if (m := re.search(r"(\d+) failed", output)) else 0
errors = int(m.group(1)) if (m := re.search(r"(\d+) error", output)) else 0
total = passed + failed + errors
success = (failed == 0 and errors == 0 and total > 0)
return {
"success": success,
"passed": passed,
"failed": failed,
"errors": errors,
"total": total,
"output": output,
}