use multiple apis
This commit is contained in:
80
app/test_runner.py
Normal file
80
app/test_runner.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
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,
|
||||
}
|
||||
Reference in New Issue
Block a user