基于结构引导的单元测试断言生成框架
传统 LLM 生成单元测试断言时存在三大挑战:
源码经过 AST/CFG/DDG 多视图融合分析,识别测试义务点,生成 Test-Oriented Graph (TOG)。
源码 → AST/CFG/DDG 多视图融合 → 红边标注测试义务点 → TOG
将全局断言生成任务分解为局部子路径覆盖任务:
| 特性 | 传统方法 | MLSP |
|---|---|---|
| 上下文长度 | O(n²) | O(n) |
| 注意力稀释 | 严重 | 有效缓解 |
| 路径覆盖 | 局部 | 全局最优 |
算法优势:
| 槽位 | 语义 | 示例 |
|---|---|---|
| AssertType | 断言方法类型 | assertEquals, assertThrows |
| Expected | 期望值 | 200, expectedUser |
| Actual | 实际执行结果 | service.calculate(x) |
| Delta | 精度容差 | 0.001 |
| Message | 失败提示 | "Calculation mismatch" |
| Precondition | 前置条件校验 | assertNotNull(input) |
| Postcondition | 后置状态验证 | verify(mock).call() |
┌─────────────┐
│ 源码输入 │
└──────┬──────┘
↓
┌─────────────┐
│ TOG 构建 │ ← 领域知识库
└──────┬──────┘
↓
┌─────────────┐
│ MLSP 分解 │
└──────┬──────┘
↓
┌─────────────┐
│ LLM 生成 │
└──────┬──────┘
↓
┌─────────────┐
│ 断言执行 │
└──────┬──────┘
↓
┌─────────────┐
│ 覆盖分析 │ → 未覆盖路径 → 返回 MLSP
└──────┬──────┘
↓
┌─────────────┐
│ 知识库更新 │
└─────────────┘
class TestObligationGraph:
def __init__(self):
self.nodes: List[CodeNode] # 代码节点
self.edges: List[Edge] # 控制流边
self.test_obligation: Set[Edge] # 测试义务边(需覆盖)
self.node_features: Dict # 节点特征
def mark_test_obligations(self):
"""标注需要断言覆盖的边"""
for edge in self.edges:
if self.is_branch_boundary(edge):
self.test_obligation.add(edge)
def mlsp_decompose(tog: TestObligationGraph) -> List[LinearSubpath]:
"""最小线性子路径分解"""
subpaths = []
obligation_edges = tog.test_obligation
for target_edge in obligation_edges:
# 提取从入口到目标边的最短路径
path = find_minimal_path(tog, target_edge)
subpaths.append(LinearSubpath(
nodes=path.nodes,
obligation_edge=target_edge,
linearized=True # 保证线性
))
return merge_overlapping(subpaths)
def fill_assertion_slots(
subpath: LinearSubpath,
llm: BaseLLM,
knowledge_base: KnowledgeBase
) -> Assertion:
"""7槽位断言模板填充"""
# 1. 检索相似案例
similar = knowledge_base.retrieve(subpath)
# 2. 构建提示
prompt = build_prompt(
code=subpath.get_code(),
template=AssertionTemplate.SEVEN_SLOT,
similar_cases=similar,
domain_knowledge=knowledge_base.get_domain()
)
# 3. LLM 生成
raw_assertion = llm.generate(prompt)
# 4. 解析填充
return parse_and_fill(raw_assertion, AssertionTemplate.SEVEN_SLOT)