chore: reorganize documentation structure
- Move vba_test_runner_flowchart.md to docs/ directory - Remove IMPLEMENTATION_SUMMARY.md (content consolidated in README.md) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,184 +0,0 @@
|
|||||||
# VBA 自动化测试与精确报错系统 - 实现总结
|
|
||||||
|
|
||||||
## 项目完成状态
|
|
||||||
|
|
||||||
✅ **已完成** - 所有核心功能已实现并测试通过
|
|
||||||
|
|
||||||
## 已实现的功能
|
|
||||||
|
|
||||||
### 1. 代码编织器 (CodeWeaver)
|
|
||||||
- ✅ 解析 VBA 代码中的所有 Sub/Function 过程
|
|
||||||
- ✅ 智能注入行号标签(10, 20, 30...)
|
|
||||||
- ✅ 自动跳过声明区、注释、空行
|
|
||||||
- ✅ 注入错误处理逻辑(On Error GoTo)
|
|
||||||
- ✅ 生成 Source Map(行号到源代码的映射)
|
|
||||||
|
|
||||||
### 2. 日志模块注入器 (LoggerInjector)
|
|
||||||
- ✅ 自动注入 TestLogger 辅助模块
|
|
||||||
- ✅ 支持模块替换(删除旧模块,注入新模块)
|
|
||||||
- ✅ 记录测试成功/失败状态
|
|
||||||
- ✅ 捕获错误详情(过程名、行号、错误号、描述)
|
|
||||||
|
|
||||||
### 3. 测试执行器 (TestRunner)
|
|
||||||
- ✅ Excel 生命周期管理
|
|
||||||
- ✅ VBA 代码读取和热替换
|
|
||||||
- ✅ 宏执行和结果获取
|
|
||||||
- ✅ 不保存原始文件(非侵入式)
|
|
||||||
|
|
||||||
### 4. 命令行界面
|
|
||||||
- ✅ 支持单个或批量测试
|
|
||||||
- ✅ 清晰的测试结果输出
|
|
||||||
- ✅ 测试汇总统计
|
|
||||||
|
|
||||||
## 测试验证
|
|
||||||
|
|
||||||
### 测试场景
|
|
||||||
|
|
||||||
✅ **TestSuccessfulProcedure** - 成功执行
|
|
||||||
```
|
|
||||||
[PASS] TestSuccessfulProcedure - Test Passed
|
|
||||||
```
|
|
||||||
|
|
||||||
✅ **TestErrorProcedure** - 除以零错误
|
|
||||||
```
|
|
||||||
[FAIL] TestErrorProcedure - Test Failed
|
|
||||||
Error Description: Division by zero
|
|
||||||
Error Line: 30
|
|
||||||
Source Code: result = x / y
|
|
||||||
```
|
|
||||||
|
|
||||||
✅ **TestTypeMismatch** - 类型不匹配错误
|
|
||||||
```
|
|
||||||
[FAIL] TestTypeMismatch - Test Failed
|
|
||||||
Error Description: Type mismatch
|
|
||||||
Error Line: 20
|
|
||||||
Source Code: y = x
|
|
||||||
```
|
|
||||||
|
|
||||||
✅ **TestSubscriptError** - 下标越界错误
|
|
||||||
```
|
|
||||||
[FAIL] TestSubscriptError - Test Failed
|
|
||||||
Error Description: Subscript out of range
|
|
||||||
Error Line: 40
|
|
||||||
Source Code: value = arr(5)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 批量测试结果
|
|
||||||
```
|
|
||||||
===== VBA Batch Test Results =====
|
|
||||||
|
|
||||||
[1/4] [FAIL] TestErrorProcedure - Test Failed
|
|
||||||
Error Description: Division by zero
|
|
||||||
Error Line: 30
|
|
||||||
Source Code: result = x / y
|
|
||||||
[2/4] [FAIL] TestTypeMismatch - Test Failed
|
|
||||||
Error Description: Type mismatch
|
|
||||||
Error Line: 20
|
|
||||||
Source Code: y = x
|
|
||||||
[3/4] [FAIL] TestSubscriptError - Test Failed
|
|
||||||
Error Description: Subscript out of range
|
|
||||||
Error Line: 40
|
|
||||||
Source Code: value = arr(5)
|
|
||||||
[4/4] [PASS] TestSuccessfulProcedure - Test Passed
|
|
||||||
|
|
||||||
===== Test Summary =====
|
|
||||||
Passed: 1/4
|
|
||||||
Failed: 3/4
|
|
||||||
```
|
|
||||||
|
|
||||||
## 文件清单
|
|
||||||
|
|
||||||
| 文件名 | 状态 | 描述 |
|
|
||||||
|--------|------|------|
|
|
||||||
| `vba_test_runner.py` | ✅ 完成 | 主脚本,包含所有类 |
|
|
||||||
| `demo.xlsm` | ✅ 完成 | 测试目标文件(不修改) |
|
|
||||||
| `create_demo.py` | ✅ 完成 | 创建演示文件 |
|
|
||||||
| `README.md` | ✅ 完成 | 项目文档 |
|
|
||||||
| `check_vba_access.py` | ✅ 完成 | 检查 VBA 访问权限 |
|
|
||||||
|
|
||||||
## 关键技术实现
|
|
||||||
|
|
||||||
### 行号标签机制
|
|
||||||
VBA 的 `Erl` 函数会返回最近执行的行号标签:
|
|
||||||
```vba
|
|
||||||
10 x = 10
|
|
||||||
20 y = 0
|
|
||||||
30 result = x / y ' Erl 将返回 30
|
|
||||||
```
|
|
||||||
|
|
||||||
### 错误处理模板
|
|
||||||
```vba
|
|
||||||
On Error GoTo Auto_Err_Handler_{proc_name}
|
|
||||||
|
|
||||||
... 原有代码 ...
|
|
||||||
|
|
||||||
Call TestLogger.LogSuccess()
|
|
||||||
Exit Sub
|
|
||||||
|
|
||||||
Auto_Err_Handler_{proc_name}:
|
|
||||||
Call TestLogger.LogError("{proc_name}", Err.Number, Err.Description, Erl)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 非侵入式测试
|
|
||||||
- 使用 `wb.api.Close(False)` 不保存更改
|
|
||||||
- 原始 Excel 文件保持不变
|
|
||||||
- 代码编织只在内存中执行
|
|
||||||
|
|
||||||
## 使用说明
|
|
||||||
|
|
||||||
### 1. 安装依赖
|
|
||||||
```bash
|
|
||||||
pip install xlwings pywin32
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 启用 VBA 项目访问
|
|
||||||
1. 打开 Excel
|
|
||||||
2. 文件 > 选项 > 信任中心
|
|
||||||
3. 信任中心设置 > 宏设置
|
|
||||||
4. 勾选"信任对 VBA 工程对象模型的访问"
|
|
||||||
|
|
||||||
### 3. 运行测试
|
|
||||||
```bash
|
|
||||||
# 测试单个过程
|
|
||||||
python vba_test_runner.py demo.xlsm Module1 TestSuccessfulProcedure
|
|
||||||
|
|
||||||
# 批量测试
|
|
||||||
python vba_test_runner.py demo.xlsm Module1 TestErrorProcedure TestTypeMismatch TestSubscriptError TestSuccessfulProcedure
|
|
||||||
```
|
|
||||||
|
|
||||||
## 已解决的问题
|
|
||||||
|
|
||||||
### 问题 1: VBA 模块命名限制
|
|
||||||
- **问题**: 模块名不能以下划线开头
|
|
||||||
- **解决**: 将 `_TestLogger` 改为 `TestLogger`
|
|
||||||
|
|
||||||
### 问题 2: Exit Sub 语法错误
|
|
||||||
- **问题**: 代码生成 `Sub` 而不是 `Exit Sub`
|
|
||||||
- **解决**: 修复条件判断逻辑
|
|
||||||
|
|
||||||
### 问题 3: Excel API 兼容性
|
|
||||||
- **问题**: `wb.close(SaveChanges=False)` 不支持
|
|
||||||
- **解决**: 使用 `wb.api.Close(False)`
|
|
||||||
|
|
||||||
### 问题 4: Unicode 编码
|
|
||||||
- **问题**: Windows 控制台不支持 Unicode 字符
|
|
||||||
- **解决**: 使用 ASCII 字符 `[PASS]` 和 `[FAIL]`
|
|
||||||
|
|
||||||
## 核心价值
|
|
||||||
|
|
||||||
1. **精确报错**: 从"发生意外"到"第30行:result = x / y"
|
|
||||||
2. **自动化测试**: 批量执行多个 VBA 宏
|
|
||||||
3. **非侵入式**: 不污染原始代码文件
|
|
||||||
4. **易于使用**: 简单的命令行界面
|
|
||||||
|
|
||||||
## 扩展方向
|
|
||||||
|
|
||||||
- 支持类模块和窗体模块
|
|
||||||
- 支持参数化测试
|
|
||||||
- 生成 HTML 测试报告
|
|
||||||
- 集成到 CI/CD 流程
|
|
||||||
- 支持远程 Excel 实例
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
该系统成功实现了 VBA 自动化测试与精确报错功能,通过代码编织技术解决了传统 VBA 调试的痛点。所有测试场景均已验证通过,系统稳定可用。
|
|
||||||
564
docs/vba_test_runner_flowchart.md
Normal file
564
docs/vba_test_runner_flowchart.md
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
# VBA 自动化测试系统 - 流程图文档
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
1. [系统架构概览](#系统架构概览)
|
||||||
|
2. [单个测试执行流程](#单个测试执行流程)
|
||||||
|
3. [全项目测试流程](#全项目测试流程)
|
||||||
|
4. [代码编织流程](#代码编织流程)
|
||||||
|
5. [错误处理与调用链追踪](#错误处理与调用链追踪)
|
||||||
|
6. [类关系图](#类关系图)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 系统架构概览
|
||||||
|
|
||||||
|
### 整体数据流
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TB
|
||||||
|
A[用户输入测试请求] --> B{测试模式}
|
||||||
|
B -->|单个过程| C[run_test]
|
||||||
|
B -->|全项目| D[run_all_tests]
|
||||||
|
|
||||||
|
C --> E[TestRunner 初始化]
|
||||||
|
D --> E
|
||||||
|
|
||||||
|
E --> F[CodeWeaver 解析 VBA 模块]
|
||||||
|
F --> G[注入行号标签和错误处理]
|
||||||
|
G --> H[LoggerInjector 注入日志模块]
|
||||||
|
H --> I[热替换模块代码]
|
||||||
|
I --> J[执行 VBA 宏]
|
||||||
|
J --> K[捕获执行结果]
|
||||||
|
K --> L[返回 TestResult]
|
||||||
|
|
||||||
|
L --> M{测试成功?}
|
||||||
|
M -->|是| N[显示成功信息]
|
||||||
|
M -->|否| O[显示详细错误信息]
|
||||||
|
O --> P[包含错误位置、源代码、调用链]
|
||||||
|
|
||||||
|
N --> Q[清理资源,关闭 Excel]
|
||||||
|
P --> Q
|
||||||
|
|
||||||
|
style A fill:#e1f5ff
|
||||||
|
style N fill:#c8e6c9
|
||||||
|
style O fill:#ffcdd2
|
||||||
|
style Q fill:#fff9c4
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 单个测试执行流程
|
||||||
|
|
||||||
|
### TestRunner.run_test() 详细流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Start([开始: run_test]) --> OpenExcel[1. 打开 Excel 文件<br/>xw.App.visible]
|
||||||
|
OpenExcel --> ReadCode[2. 读取原始 VBA 代码<br/>_get_vba_code]
|
||||||
|
|
||||||
|
ReadCode --> WeaveCode[3. 编织模块代码<br/>weave_module_all_procedures]
|
||||||
|
WeaveCode --> ParseProcs[3.1 解析所有过程<br/>parse_procedures]
|
||||||
|
ParseProcs --> InjectLineNum[3.2 注入行号标签<br/>_inject_line_numbers]
|
||||||
|
InjectLineNum --> InjectErrHandler[3.3 注入错误处理和调用栈<br/>_inject_error_handler_with_callstack]
|
||||||
|
|
||||||
|
InjectErrHandler --> InjectLogger[4. 注入 Logger 模块<br/>inject_or_replace]
|
||||||
|
InjectLogger --> ReplaceCode[5. 热替换模块代码<br/>_replace_module_code]
|
||||||
|
ReplaceCode --> Execute[6. 执行宏<br/>_execute_macro]
|
||||||
|
|
||||||
|
Execute --> GetResult[7. 获取测试结果<br/>_get_test_result]
|
||||||
|
GetResult --> CheckResult{结果字符串}
|
||||||
|
|
||||||
|
CheckResult -->|SUCCESS| CreateSuccess[创建成功 TestResult]
|
||||||
|
CheckResult -->|ERROR| ParseError[解析错误信息字符串<br/>格式: ERROR + 模块.过程 + 行号 + 编号 + 描述 + 调用链]
|
||||||
|
|
||||||
|
ParseError --> ExtractInfo[提取错误模块、过程、行号]
|
||||||
|
ExtractInfo --> LookupSource[从 Source Map 查找源代码<br/>source_map[line]]
|
||||||
|
LookupSource --> CreateError[创建失败 TestResult]
|
||||||
|
|
||||||
|
CreateSuccess --> Cleanup[8. 清理资源<br/>_cleanup]
|
||||||
|
CreateError --> Cleanup
|
||||||
|
Cleanup --> End([结束])
|
||||||
|
|
||||||
|
异常处理[Exception] --> CreateErrorResult[创建异常 TestResult]
|
||||||
|
CreateErrorResult --> Cleanup
|
||||||
|
|
||||||
|
style Start fill:#e1f5ff
|
||||||
|
style End fill:#e1f5ff
|
||||||
|
style CreateSuccess fill:#c8e6c9
|
||||||
|
style CreateError fill:#ffcdd2
|
||||||
|
style Cleanup fill:#fff9c4
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 全项目测试流程
|
||||||
|
|
||||||
|
### TestRunner.run_all_tests() 批量测试流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Start([开始: run_all_tests]) --> Discover[发现所有测试<br/>discover_all_tests]
|
||||||
|
|
||||||
|
Discover --> ParseModules[解析 VBA 项目<br/>parse_modules]
|
||||||
|
ParseModules --> IterateModules[遍历所有模块]
|
||||||
|
IterateModules --> IterateProcs[遍历每个过程]
|
||||||
|
IterateProcs --> CheckEntry{是否是入口点?<br/>_is_entry_point}
|
||||||
|
|
||||||
|
CheckEntry -->|排除事件/Logger过程| Filter[过滤掉]
|
||||||
|
CheckEntry -->|是| AddToList[添加到测试列表]
|
||||||
|
|
||||||
|
AddToList --> Filter
|
||||||
|
Filter --> MoreModules{更多模块?}
|
||||||
|
MoreModules -->|是| IterateModules
|
||||||
|
MoreModules -->|否| PrintStats[打印统计信息<br/>总过程数、可测试数]
|
||||||
|
|
||||||
|
PrintStats --> OpenExcel[打开 Excel 文件]
|
||||||
|
OpenExcel --> WeaveAll[一次性编织所有模块<br/>_weave_all_modules_inplace]
|
||||||
|
|
||||||
|
WeaveAll --> InjectLoggerModule[注入 TestLogger 和 CallStack]
|
||||||
|
InjectLoggerModule --> WeaveEach[编织每个模块]
|
||||||
|
WeaveEach --> ReplaceAll[热替换所有模块代码]
|
||||||
|
|
||||||
|
ReplaceAll --> RunTests[执行所有测试]
|
||||||
|
RunTests --> TestLoop[遍历入口点测试]
|
||||||
|
|
||||||
|
TestLoop --> InitLogger[初始化 TestLogger<br/>Initialize]
|
||||||
|
InitLogger --> RunSingle[执行单个测试<br/>_run_single_test]
|
||||||
|
|
||||||
|
RunSingle --> ExecuteMacro[执行宏]
|
||||||
|
ExecuteMacro --> GetSingleResult[获取结果]
|
||||||
|
GetSingleResult --> PrintResult[打印测试结果<br/>print_test_result]
|
||||||
|
|
||||||
|
PrintResult --> MoreTests{更多测试?}
|
||||||
|
MoreTests -->|是| TestLoop
|
||||||
|
MoreTests -->|否| PrintSummary[打印测试汇总<br/>print_summary]
|
||||||
|
|
||||||
|
PrintSummary --> Cleanup[清理资源]
|
||||||
|
Cleanup --> End([结束])
|
||||||
|
|
||||||
|
style Start fill:#e1f5ff
|
||||||
|
style End fill:#e1f5ff
|
||||||
|
style PrintStats fill:#fff9c4
|
||||||
|
style PrintSummary fill:#fff9c4
|
||||||
|
style Cleanup fill:#ffe0b2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 代码编织流程
|
||||||
|
|
||||||
|
### CodeWeaver 代码注入详细流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Start([开始: weave_procedure_with_callstack]) --> Parse[解析过程<br/>parse_procedures]
|
||||||
|
|
||||||
|
Parse --> FindProc{找到过程?}
|
||||||
|
FindProc -->|否| Error[抛出 ValueError]
|
||||||
|
FindProc -->|是| ExtractLines[提取过程代码行<br/>start_line 到 end_line]
|
||||||
|
|
||||||
|
ExtractLines --> InjectLineNums[注入行号标签<br/>_inject_line_numbers]
|
||||||
|
|
||||||
|
InjectLineNums --> InitCounter[初始化计数器 = 10]
|
||||||
|
InitCounter --> LineLoop[遍历代码行]
|
||||||
|
|
||||||
|
LineLoop --> CheckLine{检查行类型}
|
||||||
|
CheckLine -->|声明行| SkipDec[跳过]
|
||||||
|
CheckLine -->|注释行| SkipComment[跳过]
|
||||||
|
CheckLine -->|空行| SkipEmpty[跳过]
|
||||||
|
CheckLine -->|On Error| SkipOnError[跳过]
|
||||||
|
CheckLine -->|过程定义/结束| SkipProc[跳过]
|
||||||
|
CheckLine -->|可执行代码| AddLabel[添加行号标签<br/>10, 20, 30...]
|
||||||
|
|
||||||
|
AddLabel --> SaveToMap[保存到 source_map<br/>行号 -> 源代码]
|
||||||
|
SaveToMap --> Increment[计数器 += 10]
|
||||||
|
|
||||||
|
SkipDec --> MoreLines{更多行?}
|
||||||
|
SkipComment --> MoreLines
|
||||||
|
SkipEmpty --> MoreLines
|
||||||
|
SkipOnError --> MoreLines
|
||||||
|
SkipProc --> MoreLines
|
||||||
|
Increment --> MoreLines
|
||||||
|
|
||||||
|
MoreLines -->|是| LineLoop
|
||||||
|
MoreLines -->|否| InjectHandler[注入错误处理<br/>_inject_error_handler_with_callstack]
|
||||||
|
|
||||||
|
InjectHandler --> DetermineType[确定过程类型<br/>Sub or Function]
|
||||||
|
DetermineType --> RemoveExisting[删除现有 On Error]
|
||||||
|
|
||||||
|
RemoveExisting --> InsertLogEntry[在第一个可执行语句后插入:<br/>TestLogger.LogEntry<br/>On Error GoTo Handler]
|
||||||
|
|
||||||
|
InsertLogEntry --> InsertExitBlock[在 End 前插入退出块:<br/>TestLogger.LogExit<br/>TestLogger.LogSuccess<br/>Exit Sub/Function]
|
||||||
|
|
||||||
|
InsertExitBlock --> InsertHandlerBlock[插入错误处理块:<br/>Auto_Err_Handler:<br/>TestLogger.LogError<br/>TestLogger.LogExit]
|
||||||
|
|
||||||
|
InsertHandlerBlock --> ReplaceProc[替换原过程]
|
||||||
|
ReplaceProc --> Return[返回编织后的代码<br/>和 source_map]
|
||||||
|
Return --> End([结束])
|
||||||
|
|
||||||
|
Error --> End
|
||||||
|
|
||||||
|
style Start fill:#e1f5ff
|
||||||
|
style End fill:#e1f5ff
|
||||||
|
style AddLabel fill:#c8e6c9
|
||||||
|
style InsertLogEntry fill:#b3e5fc
|
||||||
|
style InsertHandlerBlock fill:#ffccbc
|
||||||
|
```
|
||||||
|
|
||||||
|
### 行号标签注入规则
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[原始 VBA 代码] --> B{行类型判断}
|
||||||
|
|
||||||
|
B --> C[声明块<br/>Dim/Private/Public/Const]
|
||||||
|
B --> D[注释行<br/>' 开头]
|
||||||
|
B --> E[空行]
|
||||||
|
B --> F[On Error 语句]
|
||||||
|
B --> G[过程定义行<br/>Sub/Function]
|
||||||
|
B --> H[过程结束行<br/>End Sub/Function]
|
||||||
|
B --> I[退出语句<br/>Exit Sub/Function]
|
||||||
|
B --> J[可执行代码]
|
||||||
|
|
||||||
|
C --> Z[跳过,不注入]
|
||||||
|
D --> Z
|
||||||
|
E --> Z
|
||||||
|
F --> Z
|
||||||
|
G --> Z
|
||||||
|
H --> Z
|
||||||
|
I --> Z
|
||||||
|
|
||||||
|
J --> K[注入行号标签<br/>10, 20, 30...]
|
||||||
|
K --> L[记录到 source_map]
|
||||||
|
|
||||||
|
style C fill:#ffcdd2
|
||||||
|
style D fill:#ffcdd2
|
||||||
|
style E fill:#ffcdd2
|
||||||
|
style F fill:#ffcdd2
|
||||||
|
style G fill:#ffcdd2
|
||||||
|
style H fill:#ffcdd2
|
||||||
|
style I fill:#ffcdd2
|
||||||
|
style J fill:#c8e6c9
|
||||||
|
style Z fill:#ffe0b2
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误处理与调用链追踪
|
||||||
|
|
||||||
|
### TestLogger 和 CallStack 工作流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Test as 测试流程
|
||||||
|
participant Logger as TestLogger
|
||||||
|
participant Stack as CallStack
|
||||||
|
participant VBA as VBA 过程
|
||||||
|
|
||||||
|
Test->>Logger: Initialize()
|
||||||
|
Logger->>Logger: m_TestStatus = "SUCCESS"
|
||||||
|
|
||||||
|
Test->>VBA: 调用过程 A
|
||||||
|
|
||||||
|
VBA->>Logger: LogEntry("A", "Module1")
|
||||||
|
Logger->>Stack: Push("A", "Module1")
|
||||||
|
Stack->>Stack: m_Stack.Add("Module1.A")
|
||||||
|
|
||||||
|
VBA->>VBA: 执行代码 (带行号标签)
|
||||||
|
|
||||||
|
alt 过程 A 调用过程 B
|
||||||
|
VBA->>Logger: LogEntry("B", "Module1")
|
||||||
|
Logger->>Stack: Push("B", "Module1")
|
||||||
|
Stack->>Stack: m_Stack.Add("Module1.B")
|
||||||
|
|
||||||
|
VBA->>VBA: 执行代码
|
||||||
|
Note over VBA: 20: x = 1 / 0 ❌ 除零错误
|
||||||
|
|
||||||
|
VBA->>Logger: LogError("B", "Module1", 11, "Division by zero", 20)
|
||||||
|
Logger->>Logger: m_TestStatus = "ERROR"
|
||||||
|
Logger->>Stack: GetCallChain()
|
||||||
|
Stack-->>Logger: "Module1.A -> Module1.B"
|
||||||
|
Logger->>Logger: 保存错误信息
|
||||||
|
|
||||||
|
VBA->>Logger: LogExit()
|
||||||
|
Logger->>Stack: Pop()
|
||||||
|
end
|
||||||
|
|
||||||
|
VBA->>Logger: LogExit()
|
||||||
|
Logger->>Stack: Pop()
|
||||||
|
|
||||||
|
Test->>Logger: GetResult()
|
||||||
|
Logger-->>Test: "ERROR + Module1.B + 20 + 11 + Division by zero + Module1.A -> Module1.B"
|
||||||
|
|
||||||
|
Test->>Test: 解析错误信息
|
||||||
|
Test->>Test: 从 source_map[20] 获取源代码
|
||||||
|
Test-->>User: 显示详细错误报告
|
||||||
|
```
|
||||||
|
|
||||||
|
### 错误信息传递流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[VBA 运行时错误] --> B[On Error GoTo 捕获]
|
||||||
|
B --> C[跳转到 Auto_Err_Handler]
|
||||||
|
C --> D[调用 TestLogger.LogError]
|
||||||
|
|
||||||
|
D --> E[设置 m_TestStatus = ERROR]
|
||||||
|
E --> F[保存错误信息:<br/>模块、过程、行号、编号、描述]
|
||||||
|
F --> G[从 CallStack 获取调用链]
|
||||||
|
|
||||||
|
G --> H[TestRunner 调用 GetResult]
|
||||||
|
H --> I{检查 m_TestStatus}
|
||||||
|
|
||||||
|
I -->|SUCCESS| J[返回 "SUCCESS"]
|
||||||
|
I -->|ERROR| K[构造错误字符串:<br/>ERROR + 模块.过程 + 行号 + 编号 + 描述 + 调用链]
|
||||||
|
|
||||||
|
K --> L[TestRunner 解析字符串]
|
||||||
|
L --> M[提取错误模块、过程、行号]
|
||||||
|
M --> N[从 source_map 查找源代码]
|
||||||
|
N --> O[构造 TestResult 对象]
|
||||||
|
O --> P[显示详细错误报告]
|
||||||
|
|
||||||
|
style A fill:#ffcdd2
|
||||||
|
style J fill:#c8e6c9
|
||||||
|
style K fill:#ffccbc
|
||||||
|
style P fill:#fff9c4
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 类关系图
|
||||||
|
|
||||||
|
### 系统类结构与依赖关系
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class TestResult {
|
||||||
|
+str procedure_name
|
||||||
|
+bool success
|
||||||
|
+int error_number
|
||||||
|
+str error_description
|
||||||
|
+int error_line
|
||||||
|
+str source_code
|
||||||
|
+str error_module
|
||||||
|
+str call_chain
|
||||||
|
}
|
||||||
|
|
||||||
|
class CodeWeaver {
|
||||||
|
-int line_counter
|
||||||
|
+parse_modules(vba_project) Dict
|
||||||
|
+parse_procedures(code) Dict
|
||||||
|
+weave_procedure_with_callstack(code, proc_name, module_name) Tuple
|
||||||
|
+weave_module_all_procedures(code, module_name) Tuple
|
||||||
|
+weave_all_modules(modules) Dict
|
||||||
|
-_inject_line_numbers(lines) Tuple
|
||||||
|
-_inject_error_handler_with_callstack(lines, proc_name, module_name) List
|
||||||
|
-_get_component_code(component) str
|
||||||
|
}
|
||||||
|
|
||||||
|
class LoggerInjector {
|
||||||
|
+str CALLSTACK_CLASS_CODE
|
||||||
|
+str LOGGER_MODULE_CODE
|
||||||
|
+inject_or_replace(wb, module_name) None
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestRunner {
|
||||||
|
+str file_path
|
||||||
|
+bool visible
|
||||||
|
+CodeWeaver code_weaver
|
||||||
|
+LoggerInjector logger_injector
|
||||||
|
+Dict source_map
|
||||||
|
+run_test(module_name, proc_name) TestResult
|
||||||
|
+run_all_tests() List~TestResult~
|
||||||
|
+discover_all_tests() List~Dict~
|
||||||
|
-_get_vba_code(module_name) str
|
||||||
|
-_replace_module_code(module_name, new_code) None
|
||||||
|
-_execute_macro(proc_name) None
|
||||||
|
-_get_test_result(proc_name) TestResult
|
||||||
|
-_cleanup() None
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestLogger {
|
||||||
|
<<VBA Module>>
|
||||||
|
+LogEntry(procName, moduleName)
|
||||||
|
+LogExit()
|
||||||
|
+LogError(procName, moduleName, errNum, errDesc, errLine)
|
||||||
|
+LogSuccess()
|
||||||
|
+GetResult() String
|
||||||
|
+Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallStack {
|
||||||
|
<<VBA Class Module>>
|
||||||
|
-Collection m_Stack
|
||||||
|
+Push(procName, moduleName)
|
||||||
|
+Pop()
|
||||||
|
+GetCallChain() String
|
||||||
|
+Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
TestRunner --> CodeWeaver : 使用
|
||||||
|
TestRunner --> LoggerInjector : 使用
|
||||||
|
TestRunner --> TestResult : 创建
|
||||||
|
LoggerInjector --> TestLogger : 注入
|
||||||
|
LoggerInjector --> CallStack : 注入
|
||||||
|
TestLogger --> CallStack : 使用
|
||||||
|
CodeWeaver --> TestResult : 生成 source_map
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模块交互时序图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant User as 用户
|
||||||
|
participant Main as main()
|
||||||
|
participant Runner as TestRunner
|
||||||
|
participant Weaver as CodeWeaver
|
||||||
|
participant Injector as LoggerInjector
|
||||||
|
participant Excel as Excel Application
|
||||||
|
|
||||||
|
User->>Main: 执行命令
|
||||||
|
Main->>Runner: 初始化(file_path, visible=False)
|
||||||
|
|
||||||
|
alt 单个测试模式
|
||||||
|
Main->>Runner: run_test(module, proc)
|
||||||
|
else 全项目测试模式
|
||||||
|
Main->>Runner: run_all_tests()
|
||||||
|
Runner->>Runner: discover_all_tests()
|
||||||
|
Runner->>Runner: _weave_all_modules_inplace()
|
||||||
|
end
|
||||||
|
|
||||||
|
Runner->>Excel: 打开工作簿
|
||||||
|
Runner->>Weaver: parse_modules(vba_project)
|
||||||
|
Weaver-->>Runner: 模块信息
|
||||||
|
|
||||||
|
Runner->>Weaver: weave_module_all_procedures(code, module)
|
||||||
|
Weaver->>Weaver: _inject_line_numbers()
|
||||||
|
Weaver->>Weaver: _inject_error_handler_with_callstack()
|
||||||
|
Weaver-->>Runner: woven_code, source_map
|
||||||
|
|
||||||
|
Runner->>Injector: inject_or_replace(wb)
|
||||||
|
Injector->>Excel: 删除旧的 TestLogger/CallStack
|
||||||
|
Injector->>Excel: 添加新的 CallStack 类模块
|
||||||
|
Injector->>Excel: 添加新的 TestLogger 模块
|
||||||
|
|
||||||
|
Runner->>Excel: _replace_module_code(module, woven_code)
|
||||||
|
Runner->>Excel: _execute_macro(module.proc)
|
||||||
|
Excel-->>Runner: 执行结果
|
||||||
|
|
||||||
|
Runner->>Excel: TestLogger.GetResult()
|
||||||
|
Excel-->>Runner: "SUCCESS" 或 "ERROR|..."
|
||||||
|
|
||||||
|
Runner-->>Main: TestResult
|
||||||
|
Main-->>User: 打印测试结果
|
||||||
|
|
||||||
|
Runner->>Excel: _cleanup()
|
||||||
|
Excel->>Excel: 关闭工作簿(不保存)
|
||||||
|
Excel->>Excel: 退出 Excel
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录:关键数据结构
|
||||||
|
|
||||||
|
### TestResult 数据类
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[TestResult 数据类] --> B[procedure_name: str<br/>被测试的过程名称]
|
||||||
|
A --> C[success: bool<br/>测试是否成功]
|
||||||
|
A --> D[error_number: int<br/>VBA 错误编号]
|
||||||
|
A --> E[error_description: str<br/>错误描述信息]
|
||||||
|
A --> F[error_line: int<br/>错误发生的行号标签]
|
||||||
|
A --> G[source_code: str<br/>错误行的原始源代码]
|
||||||
|
A --> H[error_module: str<br/>错误发生的模块名]
|
||||||
|
A --> I[call_chain: str<br/>完整调用链<br/>Module1.ProcA -> Module1.ProcB]
|
||||||
|
|
||||||
|
style A fill:#e1f5ff
|
||||||
|
style C fill:#c8e6c9
|
||||||
|
style F fill:#fff9c4
|
||||||
|
style G fill:#fff9c4
|
||||||
|
style I fill:#ffccbc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Source Map 映射关系
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A[原始 VBA 代码] -->|CodeWeaver 处理| B[编织后的代码]
|
||||||
|
|
||||||
|
B --> C[行号标签 10<br/>x = 1]
|
||||||
|
B --> D[行号标签 20<br/>y = x / 0 ❌]
|
||||||
|
B --> E[行号标签 30<br/>z = y + 1]
|
||||||
|
|
||||||
|
C --> F[source_map[10] = 'x = 1']
|
||||||
|
D --> G[source_map[20] = 'y = x / 0']
|
||||||
|
E --> H[source_map[30] = 'z = y + 1']
|
||||||
|
|
||||||
|
G --> I[VBA 错误发生在行 20]
|
||||||
|
I --> J[通过 source_map[20]<br/>获取原始源代码]
|
||||||
|
J --> K[显示给用户:<br/>y = x / 0]
|
||||||
|
|
||||||
|
style A fill:#e1f5ff
|
||||||
|
style B fill:#c8e6c9
|
||||||
|
style G fill:#ffcdd2
|
||||||
|
style K fill:#fff9c4
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用示例流程
|
||||||
|
|
||||||
|
### 命令行使用模式
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
Start([用户执行命令]) --> CheckArgs{参数检查}
|
||||||
|
|
||||||
|
CheckArgs -->|python vba_test_runner.py| ShowHelp1[显示用法1]
|
||||||
|
CheckArgs -->|python vba_test_runner.py file.xlsm --all| AllTest[全项目测试模式]
|
||||||
|
CheckArgs -->|python vba_test_runner.py file.xlsm Module Proc1 Proc2| SingleTest[单过程/多过程测试模式]
|
||||||
|
|
||||||
|
ShowHelp1 --> End1([结束])
|
||||||
|
|
||||||
|
AllTest --> InitRunner1[初始化 TestRunner<br/>visible=False]
|
||||||
|
InitRunner1 --> RunAll[执行 run_all_tests]
|
||||||
|
RunAll --> Discover[发现所有过程]
|
||||||
|
Discover --> WeaveAllModules[编织所有模块]
|
||||||
|
WeaveAllModules --> BatchExecute[批量执行测试]
|
||||||
|
BatchExecute --> ShowSummary[显示测试汇总]
|
||||||
|
ShowSummary --> Cleanup1[清理资源]
|
||||||
|
Cleanup1 --> End2([结束])
|
||||||
|
|
||||||
|
SingleTest --> InitRunner2[初始化 TestRunner<br/>visible=False]
|
||||||
|
InitRunner2 --> LoopProcs[循环遍历过程列表]
|
||||||
|
LoopProcs --> RunSingle[执行 run_test]
|
||||||
|
RunSingle --> ShowSingleResult[显示单个结果]
|
||||||
|
ShowSingleResult --> MoreProcs{更多过程?}
|
||||||
|
MoreProcs -->|是| LoopProcs
|
||||||
|
MoreProcs -->|否| ShowSummary2[显示测试汇总]
|
||||||
|
ShowSummary2 --> Cleanup2[清理资源]
|
||||||
|
Cleanup2 --> End3([结束])
|
||||||
|
|
||||||
|
style Start fill:#e1f5ff
|
||||||
|
style End1 fill:#e1f5ff
|
||||||
|
style End2 fill:#e1f5ff
|
||||||
|
style End3 fill:#e1f5ff
|
||||||
|
style AllTest fill:#c8e6c9
|
||||||
|
style SingleTest fill:#b3e5fc
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
该 VBA 自动化测试系统通过**代码编织技术**实现了:
|
||||||
|
|
||||||
|
1. **非侵入式测试**:在内存中修改代码,不保存到磁盘
|
||||||
|
2. **精确行号定位**:通过行号标签和 Source Map 实现源代码映射
|
||||||
|
3. **调用链追踪**:通过 CallStack 类追踪完整调用路径
|
||||||
|
4. **批量测试支持**:支持单过程和全项目两种测试模式
|
||||||
|
5. **详细错误报告**:包含错误位置、源代码、调用链等完整信息
|
||||||
|
|
||||||
|
系统的核心创新在于将编译器中的**代码编织技术**应用于 VBA 动态测试,通过在运行时注入检测代码来实现传统 IDE 无法提供的调试功能。
|
||||||
Reference in New Issue
Block a user