基于静态分析与 LLM 的测试脚本生成系统
基于静态分析与 LLM 的 AUTOSAR 测试脚本生成系统
定位:这不是一个简单的“调用大模型写代码”的 Demo,而是一个面向 AUTOSAR/车载测试场景的工程化脚本生成流水线。它把测试用例、已有测试资产、本地三方库 API、项目代码风格和 LLM 代码补全组合起来,形成了一个可审计、可回滚、可校验、可扩展的自动化测试脚本生成方案。
1. 项目背景:为什么要做 AUTOSAR 测试脚本生成
在 AUTOSAR 或车载嵌入式软件测试中,测试脚本往往不是从零开始写的。实际项目里通常同时存在以下约束:
- 测试用例来源于 Excel、需求文档或测试设计文档;
- 测试脚本必须符合项目已有 pytest/unittest 风格;
- 可调用的业务 API 来自本地测试框架或三方库;
- 不允许大模型凭空编造 API、fixture、变量名或 import;
- 生成后必须至少通过 Python 语法检查和 pytest collect;
- 最终交付的不只是代码,还需要报告、备份和错误定位信息。
所以该系统采用的是“静态分析 + 检索增强 + 骨架约束 + LLM 补全 + 自动校验”的分层方案,而不是把测试用例直接丢给 LLM 让它自由生成。
2. 系统总体架构
该模块位于:
platform/autosar_testscript_generator/
整体职责是:根据测试用例、三方库代码和已有测试脚本,先生成 Python 测试脚本骨架,再调用 LLM 补全具体测试逻辑,并执行语法检查和 pytest collect。
模块可以拆成五层:
输入层
├─ Excel 测试用例转换
└─ 测试用例 JSON 加载
静态知识层
├─ 本地三方库 AST 分析
├─ 已有测试脚本风格分析
└─ 已有测试脚本示例索引
骨架生成层
├─ 相关 API 检索
├─ 相似脚本示例检索
└─ pytest 脚本骨架生成
LLM 补全层
├─ Prompt 模板渲染
├─ LLM 生成完整 Python 文件
├─ Python 代码提取
└─ 脚本备份与写回
质量保障层
├─ py_compile 语法检查
├─ pytest --collect-only 检查
└─ 端到端报告输出
这套设计的核心思想是:把确定性的工程上下文尽量交给代码完成,把不确定的业务逻辑补全交给 LLM,但用骨架、可用 API、相似示例和校验器约束 LLM 的输出边界。
3. 对外入口:平台 Workflow 适配
系统对外暴露的工作流名称是:
autosar.testscript_generation
入口适配器是:
platform/autosar_testscript_generator/workflow.py
其中 AutosarTestscriptGenerationWorkflow 继承平台统一的 Workflow,在 run() 中读取 Task.input_payload,调用 _run_autosar_testscript_generation_impl(**payload),然后把结果封装成 TaskResult。
这一层的意义是:
- 把测试脚本生成能力挂到平台统一编排体系;
- 对外隐藏内部复杂流程;
- 统一返回 artifacts,例如
script_path、backup_script_path、report_path; - 统一 summary,例如
case_id、llm_used、related_api_count、similar_example_count。
可以理解为:workflow.py 是平台协议层,真正的业务流程在 run.py。
4. 端到端主流程:run.py 的职责边界
核心函数是:
run_autosar_testscript_generation(...)
它对外保持稳定签名,内部通过平台 orchestrator 构造 Task 并调度 workflow。真正执行逻辑在:
_run_autosar_testscript_generation_impl(...)
它的主流程如下:
1. 解析路径
2. 校验 testcase_json_path、library_dir、existing_tests_path、prompt 文件是否存在
3. 按 case_id 加载单条测试用例
4. 调用 build_skeleton_context_for_case 生成骨架上下文
5. 检查 library_index 是否扫描到 Python 文件
6. 检查 style_guide 是否扫描到已有测试脚本
7. 检查 available_apis 是否为空
8. 调用 generate_script_from_context 进行 LLM 补全
9. 汇总最终结果
10. 可选保存端到端报告
这里有几个设计亮点。
4.1 路径统一解析
_resolve_path() 会把用户传入的相对路径统一解析到仓库根目录下,避免本地调试、平台调用、命令行调用出现路径基准不一致的问题。
这在工程项目中很关键,因为测试脚本生成依赖多个目录:
testcase_json_path
default_library_dir
existing_test_script_path
output_dir
generator.yaml
任何一个路径错了,后面都会出现难定位的错误。因此代码在前置阶段就执行 _require_existing_path(),提前暴露问题。
4.2 失败前置化
主流程会提前判断:
- 测试用例 JSON 是否存在
- 三方库目录是否存在
- 已有测试脚本目录是否存在
- prompt YAML 是否存在
- library_index 是否扫描到 Python 文件
- style_guide 是否扫描到测试脚本
- available_apis 是否为空
这说明该系统不是“失败了再看大模型输出”,而是在调用 LLM 前尽量确保输入上下文是有效的。
4.3 中间结果内存传递
run.py 中明确说明:端到端流程会在内存里传递 testcase、API 摘要、相似示例和风格摘要,避免刚写入磁盘又读回。
这个设计能减少 IO 耦合,也降低中间文件状态不一致的风险。save_intermediate 只用于调试产物,不影响主链路数据传递。
5. 输入层:Excel 测试用例到标准 JSON
模块支持把 Excel 测试用例转换成标准 JSON,入口是:
format_conversion/excel_to_json.py
核心函数:
convert_excel_to_testcases(excel_path, output_path=None, sheet_name=DEFAULT_SHEET_NAME)
流程是:
read_excel_rows
-> normalize_rows
-> aggregate_testcases
-> validate_testcases
-> 输出 {"测试用例": [...]}
其中 aggregate_testcases() 会按 case_id 聚合多行步骤,避免每一行 Excel 都变成一个重复测试用例。
输出的单条测试用例结构包括:
{
"测试用例编号": "...",
"相关需求编号": "...",
"测试用例名称": "...",
"测试目的": "...",
"需求依据": [],
"前提条件": [],
"测试步骤": [
{
"step_id": 1,
"操作": "...",
"输入": null,
"预期结果": "..."
}
],
"预期结果": "...",
"优先级": "..."
}
面试中可以强调:输入层做了归一化和 schema 校验,后续生成逻辑不直接依赖 Excel 的原始格式。
6. 静态知识层:为什么要做 AST 分析
6.1 本地三方库分析
核心入口:
analyze_libraries(library_dirs, output_path=None)
这个函数会递归扫描三方库目录下的 .py 文件,对每个文件调用:
analyze_python_file(file_path, analysis_root)
然后生成 library_index:
{
"library_dirs": [],
"summary": {
"python_files": 0,
"functions": 0,
"classes": 0,
"methods": 0,
"failed_files": 0
},
"symbols": [],
"files": [],
"errors": []
}
symbols 里保存函数、类、方法等符号信息,包括:
name
qualified_name
kind
signature
parameters
return_annotation
docstring
leading_comments
source_preview
file_path
module_path
import_statement
lineno
end_lineno
keywords
6.2 为什么用 AST,而不是正则扫代码
python_ast_analyzer.py 使用 Python 标准库 ast 解析代码,不执行被分析代码。这样有几个好处:
- 安全:分析第三方库时不会 import 或执行其中逻辑;
- 结构化:能准确识别函数、类、方法、参数、返回注解、docstring;
- 可追溯:能保留
lineno、end_lineno、source_preview; - 可构造 import:能根据 module path 生成候选 import;
- 可用于检索:能把函数名、签名、docstring、注释、源码片段转成关键词。
面试中可以这样讲:
我没有把本地库代码直接全部丢给大模型,而是先通过 AST 构建符号索引,再只选与当前测试用例相关的 API 摘要进入 prompt。这样既降低 token 成本,也减少大模型误用无关 API 的概率。
7. 已有测试脚本风格分析
核心入口:
analyze_test_script_style(existing_tests_dir, output_path=None)
它会扫描已有测试脚本,提取项目真实风格,包括:
- 使用 pytest 还是 unittest
- 文件命名模式
- 是否使用 class-based pytest
- 是否使用 fixture
- 常见 import
- setup/teardown 风格
- logger 风格
- assertion 风格
- 典型测试函数命名
- 代表性参考脚本
输出结构里包含:
{
"framework": "pytest",
"file_naming": {},
"test_structure": {},
"common_imports": [],
"fixtures": {},
"setup_teardown": [],
"logging": {},
"assertions": {},
"naming_examples": {},
"reference_script": {},
"summary": {}
}
它还通过 _StyleVisitor(ast.NodeVisitor) 识别:
pytest.fixture
pytest mark
test_ 开头函数
Test 开头类
unittest.TestCase
logger.info / logging.info / print
assert / self.assert / custom assert
setup_method / teardown_method / setUp / tearDown
这部分是项目的一个优势点:生成脚本不是只关注“能不能跑”,还关注“像不像原项目的人写的”。
8. 示例索引:从已有脚本中学习调用方式
核心入口:
build_script_examples_index(existing_tests_dir, output_path=None)
它会从已有脚本中提取测试函数或测试类方法,生成示例索引:
{
"examples": [
{
"file_path": "...",
"test_class_name": "...",
"test_function_name": "...",
"docstring": "...",
"step_logs": [],
"source_preview": "...",
"local_api_calls": [],
"imports": [],
"lineno": 1,
"end_lineno": 80
}
]
}
它不仅保存源码片段,还提取:
- logger.info 中的步骤日志
- 本地 API 调用名
- import 语句
- 测试函数名
- docstring
这使后续 LLM 补全时可以参考真实项目已有脚本,而不是只根据抽象需求生成代码。
9. 检索层:相关 API 和相似脚本示例
检索模块位于:
generate_skeleton/knowledge/retriever.py
它提供两个核心函数:
retrieve_related_symbols(testcase, library_index, top_k=10)
retrieve_similar_script_examples(testcase, script_examples_index, top_k=5)
当前实现是轻量关键词匹配:
- 从测试用例标题、模块、用例编号、测试目的、前提条件、步骤操作、预期结果中抽取 token;
- 从 API 符号或示例脚本中抽取 token;
- 根据 token overlap 评分;
- 返回 top_k。
虽然这不是向量检索,但它非常适合离线内网环境:
无需 embedding 服务
无需数据库
无需网络
可解释
可快速定位为什么某个 API 被选中
面试中可以主动说明:
这个版本的检索是工程优先的轻量实现,优点是稳定、可解释、部署成本低;后续可以替换成 BM25、向量检索或混合检索,但接口层已经把检索结果抽象成 related_symbols 和 similar_examples,替换成本很低。
10. 骨架生成:先确定结构,再让 LLM 补逻辑
核心入口:
generate_test_skeleton(testcase, style_guide, related_symbols, similar_examples)
骨架生成阶段不会实现具体业务逻辑,而是负责生成一个合法的、风格一致的 pytest/unittest 脚本框架。
它会优先使用已有脚本中的 reference_script:
如果已有脚本中能找到参考测试函数:
-> 复制其 class/function 组织方式
-> 保留 decorators
-> 保留 fixture 参数
-> 复用 setup 片段
否则:
-> 根据 style_guide 生成默认 function/class/unittest 骨架
骨架内容包括:
- import
- Candidate import 注释
- class 或 function
- docstring
- Step 日志
- TODO action
- Expected 注释
- Candidate API
- pass
例如骨架中的每一步都会生成:
tester.logger.info("Step 1: ...")
# TODO: implement action: ...
# Expected: ...
# Candidate API: xxx(...)
这一层的价值是:把测试用例步骤顺序、日志、候选 API、函数名、类名先固定下来,降低 LLM 输出结构漂移。
11. Prompt 设计:只给模型必要上下文
LLM 补全阶段入口:
generate_script_from_context(...)
它会加载 YAML prompt,然后渲染以下上下文:
testcase_json
skeleton_code
available_apis_json
similar_examples_json
style_guide_summary_json
generation_rules
prompt_loader 中显式禁止使用这些占位符:
library_index_json
script_examples_index_json
style_guide_json
这说明系统不允许把完整索引全部塞给模型,而是只允许传“压缩后的上下文”。
这是很重要的工程控制:
- 控制 prompt 体积;
- 降低无关 API 干扰;
- 避免泄露过多项目代码;
- 强迫模型只基于可用 API 和相似示例生成;
- 方便调试输入上下文。
12. 生成规则:限制 LLM 不乱写
build_generation_rules() 会向 prompt 注入规则,例如:
- 基于当前 skeleton_code 补全代码,不要重建无关结构
- 保留原有测试类名、测试函数名、pytest mark、fixture 参数和 docstring
- 按 testcase.steps 的顺序实现测试逻辑
- 优先参考 similar_examples 的代码风格和调用方式
- 优先使用 available_apis 中的 API
- 不要编造上下文中没有出现过的业务 API
- 不确定 index、subindex、expected value、EDS_Config key 时不要猜测,必须生成 TODO
- 生成代码必须是合法 Python
这部分可以作为面试亮点:
我不是让 LLM 自由发挥,而是把模型放在一个受控代码补全环境里。骨架负责结构,API 摘要负责能力边界,相似示例负责风格,generation_rules 负责安全约束,校验器负责质量闭环。
13. LLM 输出解析与代码校验
llm_script_generator.py 负责调用项目统一的 LLM adapter:
call_llm(node_name, system_prompt, user_prompt, **kwargs)
然后从模型输出中提取 Python 代码。
它支持三种输出格式:
1. JSON:{"python_code": "..."}
2. Markdown fenced code:```python ... ```
3. 裸 Python 代码
提取后会调用:
ast.parse(code)
确认是合法 Python 代码。如果不是合法 Python,会直接抛错。
这避免了常见的大模型问题:
- 输出解释文字
- 输出 Markdown 但不是代码
- 输出 JSON 但字段不对
- 输出代码片段但不可解析
14. 写回策略:先备份,再覆盖
LLM 生成完整脚本后,会先执行:
backup_script(skeleton_script_path)
然后:
write_script(skeleton_script_path, generated_code)
这样即使 LLM 生成结果不可用,也能保留骨架脚本的备份。
这在工程上很重要,因为骨架是确定性生成结果,LLM 输出是不确定性结果。备份机制让系统具备可回滚能力。
15. 质量保障:语法检查和 pytest collect
该系统有两层校验。
15.1 Python 语法检查
syntax_checker.py 使用:
py_compile.compile(...)
检查生成文件语法是否合法。
它还特意把 .pyc 写到临时目录,避免污染输出目录。这是一个细节,但能体现工程洁癖:
生成流程的校验副产物不应该留在用户输出目录里。
15.2 pytest collect 检查
pytest_checker.py 使用:
python -m pytest --collect-only -p no:cacheprovider generated_file.py
并设置:
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1
PYTHONDONTWRITEBYTECODE=1
这样做的目的:
- 只检查 pytest 能否收集测试,不真正执行硬件相关测试;
- 禁用插件自动加载,减少外部环境干扰;
- 禁止写字节码,避免污染输出;
- collect 失败时不一定中断流程,而是记录错误和 stdout/stderr。
对于车载测试脚本,这很合理:很多测试依赖硬件、总线、仿真器或专用库,本地环境不完整时 collect 失败可能只是依赖缺失,而不是脚本逻辑错误。
16. 最终输出与报告
最终结果由 _build_final_result() 汇总:
{
"case_id": "...",
"script_path": "...",
"backup_script_path": "...",
"llm_used": true,
"syntax_check": {},
"pytest_collect": {},
"related_api_count": 0,
"similar_example_count": 0,
"save_intermediate": false,
"notes": []
}
如果 save_intermediate=True,还会保存端到端报告;如果为 false,输出目录只保留最终脚本和备份文件,减少中间调试产物污染。
17. 工程亮点总结
17.1 不是单纯 Prompt Engineering,而是工程化生成流水线
系统不是直接把测试用例发给 LLM,而是先建立结构化上下文:
测试用例
+ 本地库 API 索引
+ 项目测试风格
+ 相似脚本示例
+ 骨架代码
+ 生成规则
然后再由 LLM 补全。
17.2 静态分析优先,降低 LLM 幻觉
通过 AST 提取可用 API,并在 prompt 中明确要求只能使用 available_apis 和 similar_examples 中出现过的业务 API。
17.3 骨架先行,减少结构漂移
先生成确定性的测试函数、步骤日志、TODO、候选 API,再让 LLM 补业务逻辑。
17.4 风格学习,贴近真实项目代码
系统会分析已有测试脚本的 framework、fixture、import、assertion、logger、class/function 结构,让生成结果更像现有项目。
17.5 可审计、可回滚、可校验
系统有:
- library_index
- style_guide
- script_examples_index
- generation_report
- llm_generation_report
- backup_script_path
- syntax_check
- pytest_collect
这些都能帮助定位问题。
17.6 内网部署友好
当前检索使用关键词匹配,不依赖外部 embedding 服务;LLM adapter 也通过项目统一封装接入,适合企业内网环境扩展。
18. 面试中可以这样讲这个项目
18.1 一分钟版本
我做的是一个 AUTOSAR 自动化测试脚本生成系统。它不是简单调用大模型,而是先把 Excel/JSON 测试用例标准化,然后静态分析本地测试框架库,抽取可用 API;同时分析已有测试脚本,学习项目的 pytest 风格、fixture、import 和断言习惯。之后系统根据测试用例检索相关 API 和相似脚本,先生成一个确定性的测试脚本骨架,再把骨架、API 摘要、相似示例和生成规则交给 LLM 补全。最后会备份脚本,写回结果,并执行 Python 语法检查和 pytest collect。整个流程强调可控、可审计和低幻觉。
18.2 三分钟版本
这个项目的核心问题是:车载 AUTOSAR 测试脚本依赖本地测试框架、已有脚本风格和具体测试步骤,直接让大模型生成很容易编造 API 或写出不符合项目风格的代码。所以我把系统拆成两个阶段。
第一阶段是确定性的骨架生成。系统会读取测试用例,然后用 AST 静态分析本地三方库,抽取函数、类、方法、签名、docstring、import 语句和源码预览;同时分析已有测试脚本,识别 pytest/unittest 风格、fixture、logger、assertion、setup/teardown 和参考脚本。然后用关键词检索当前 case 相关 API 和相似脚本示例,生成一个包含步骤日志、TODO、Expected 和 Candidate API 的脚本骨架。
第二阶段才调用 LLM。Prompt 中只传压缩后的 testcase、skeleton、available_apis、similar_examples、style_guide_summary 和 generation_rules,不传完整索引。生成规则明确要求保留骨架结构、按测试步骤顺序实现、优先使用可用 API、不要编造 API,不确定的数据必须保留 TODO。LLM 输出后会提取 Python 代码并用 ast.parse 校验,再备份骨架、写回脚本,最后用 py_compile 和 pytest collect 做质量检查。
这个架构的价值是把 LLM 从“自由写代码”变成“受约束的代码补全器”,既利用模型能力,又保留工程可控性。
19. 面试官可能追问与回答
Q1:为什么不用 LLM 直接从测试用例生成脚本?
直接生成最大的问题是不可控。测试脚本依赖项目本地 API、fixture、import、日志风格和断言风格,LLM 不知道这些约束就容易编造不存在的 API。我的方案先用 AST 分析本地库,得到真实可调用 API;再分析已有测试脚本,得到真实项目风格;最后只把压缩后的上下文给模型。这样生成结果更稳定,也更容易排查问题。
Q2:为什么选择 AST 分析,而不是 import 后 introspection?
因为 import 可能产生副作用,尤其车载测试框架里可能初始化硬件、驱动、总线连接或读取环境变量。AST 分析只解析源码,不执行代码,更安全。而且 AST 可以拿到函数签名、参数、docstring、类方法、行号和源码片段,足够构建 API 索引。
Q3:关键词检索会不会太简单?
当前关键词检索是第一版工程可用方案。它的优势是无外部依赖、可解释、适合内网环境。接口上已经把检索结果抽象成 related_symbols 和 similar_examples,后续可以替换成 BM25、向量检索或混合检索。也就是说,当前实现简单,但架构上是可替换的。
Q4:如何减少 LLM 幻觉?
主要有五层控制:
- 骨架先生成,限制结构;
- Prompt 只传可用 API 和相似示例,不传无限上下文;
- generation_rules 明确禁止编造 API;
- 不确定 index/subindex/expected value 时要求保留 TODO;
- 生成后用 ast.parse、py_compile、pytest collect 校验。
Q5:pytest collect 失败是不是代表生成失败?
不一定。车载测试脚本经常依赖专用硬件、仿真环境、供应商库或现场测试框架。本地环境缺依赖时,collect 可能失败。系统会记录 return_code、stdout、stderr 和错误摘要,而不是简单认为脚本逻辑一定错误。真正判断时要结合错误类型:如果是语法错误,问题严重;如果是 No module named xxx,可能只是环境缺失。
Q6:为什么要生成骨架,而不是让 LLM 生成完整文件?
骨架是确定性结果,可以确保测试函数名、类结构、步骤顺序、日志、TODO 和候选 API 都是可控的。LLM 只负责把 TODO 补成具体调用逻辑。这样即使 LLM 生成失败,也能保留一个可审计、可人工继续开发的骨架脚本。
Q7:如何处理已有测试脚本风格?
系统会扫描已有测试脚本,识别:
pytest/unittest
class-based/function-based
fixture
setup/teardown
logger
assertion
common imports
reference script
生成骨架时优先复用 reference script 的结构。如果没有 reference script,则根据 style_guide 生成默认骨架。
Q8:你怎么保证大模型只使用存在的 API?
技术上无法 100% 保证,但我做了多层约束:
- Prompt 明确要求只使用 available_apis 和 similar_examples 中的 API;
- available_apis 是 AST 静态分析得到的真实 API;
- generation_rules 要求不确定时保留 TODO;
- 可以继续扩展静态校验:解析生成代码中的 Call 节点,与 available_apis 做白名单比对。
最后一点是我会主动提的改进方向,说明我知道系统边界。
Q9:系统当前最大短板是什么?
短板主要有三个:
- 关键词检索召回能力有限;
- 生成后还缺少 API 白名单级别的静态校验;
- 测试数据如 index、subindex、expected value 仍依赖测试用例质量。
但这些短板都可以工程化改进:检索层可以升级为混合检索;生成后可以加 AST call 校验;输入层可以加强测试用例 schema 和字段抽取。
Q10:这个项目最体现你能力的地方是什么?
我认为是把 LLM 能力落到工程系统里的方式。这个项目不是单点调用模型,而是完整考虑了输入标准化、静态知识构建、检索、风格学习、骨架约束、LLM 补全、代码提取、备份、语法检查、pytest collect 和报告输出。它体现的是工程闭环能力,而不是单纯 prompt 能力。
20. 可继续优化方向
20.1 API 白名单静态校验
生成脚本后用 AST 再解析一次,抽取所有函数调用:
tester.xxx()
xxx()
module.xxx()
然后与 available_apis 和 similar_examples.local_api_calls 做白名单比对。如果发现未授权业务 API,可以标记 warning 或要求二次修复。
20.2 混合检索
当前基于 token overlap,可以升级为:
BM25 + 关键词
向量检索 + rerank
API 名称精确匹配
测试步骤级别检索
20.3 失败自修复
如果 syntax_check 或 pytest_collect 失败,可以把错误摘要、原始生成代码、可用 API 再次交给 LLM 做一次受控修复。
20.4 测试数据抽取增强
对 CANopen/AUTOSAR 常见字段增强抽取,例如:
index
subindex
object dictionary
expected value
timeout
NMT state
SDO/PDO direction
这样可以减少 LLM 需要猜测的空间。
20.5 可视化报告
把 related_api_count、similar_example_count、syntax_check、pytest_collect、缺失依赖、TODO 数量做成报告,方便测试负责人评估生成质量。
21. 总结
这个 AUTOSAR 测试脚本生成系统的核心价值是:让 LLM 在受控工程上下文中完成代码补全,而不是让 LLM 自由生成测试脚本。
它通过以下设计降低风险:
输入标准化
静态 AST 分析
已有脚本风格学习
相关 API 检索
相似脚本检索
确定性骨架生成
受控 Prompt 渲染
LLM Python 代码提取
脚本备份写回
py_compile 语法检查
pytest collect 校验
报告输出
在面试中,我会把它定位为一个“面向车载测试场景的 AI 辅助代码生成平台能力”,重点强调三点:
- 懂业务场景:AUTOSAR 测试脚本依赖本地 API、已有脚本风格和硬件环境;
- 懂工程架构:不是简单调用模型,而是分层流水线和可审计闭环;
- 懂风险控制:通过 AST、骨架、白名单上下文、校验器和报告降低大模型幻觉风险。
更多推荐


所有评论(0)