Files
demo2-qwen/app/test_runner.py
2026-03-10 10:35:03 +08:00

81 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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,
}