Compare commits

...

2 Commits

Author SHA1 Message Date
Misaka
d9e11ff7dd 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>
2026-02-04 23:38:22 +08:00
Misaka
15e9fabf25 docs: update README.md with current project structure and features
- Add project status and completion indicator
- Add comprehensive project structure documentation
- Enhance installation instructions with virtual environment setup
- Add development tools section with VS Code debugging configuration
- Document --all flag for full project testing
- Add references to flowchart and implementation summary documentation
- Update FAQ with new questions about debugging and virtual environment
- Reflect current implementation features (call chain tracking, class module support, etc.)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 23:37:10 +08:00
3 changed files with 873 additions and 227 deletions

View File

@@ -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 调试的痛点。所有测试场景均已验证通过,系统稳定可用。

352
README.md
View File

@@ -1,24 +1,72 @@
# VBA 自动化测试与精确报错系统
通过代码编织Code Weaving技术实现 VBA 宏的精确到行的代码报错定位。
通过代码编织Code Weaving技术实现 VBA 宏的精确到行的代码报错定位,并支持完整的调用链追踪
## 项目状态
**已完成** - 所有核心功能已实现并测试通过
## 核心特性
- **精确行号定位**: 捕获 VBA 错误的具体行号,不再只是"发生意外"
- **源代码映射**: 通过 Source Map 机制显示出错行的原始代码
- **自动化测试**: 批量执行 VBA 宏并收集结果
- **调用链追踪**: 完整记录过程调用栈,追踪错误传播路径
- **全项目测试**: 自动发现并测试所有可测试的过程
- **模块级支持**: 支持标准模块和类模块的测试
- **非侵入式**: 测试过程不修改原始 Excel 文件
- **详细报告**: 提供清晰的测试结果输出
- **详细报告**: 提供清晰的测试结果输出,包含错误位置、源代码和调用链
## 项目结构
```
xlwings/
├── vba_test_runner.py # 主脚本 - 核心测试系统实现
├── demo.xlsm # 演示 Excel 文件(包含测试用例)
├── README.md # 项目文档(本文件)
├── vba_test_runner_flowchart.md # 详细流程图文档Mermaid 图表)
├── IMPLEMENTATION_SUMMARY.md # 实现总结与技术细节
├── CLAUDE.md # Claude Code 开发指南
├── .gitignore # Git 忽略规则
├── .vscode/
│ └── launch.json # VS Code 调试配置
└── .venv/ # Python 虚拟环境(需自行创建)
```
## 安装依赖
### 1. 创建虚拟环境(推荐)
```bash
# Windows
python -m venv .venv
.venv\Scripts\activate
# Linux/Mac
python3 -m venv .venv
source .venv/bin/activate
```
### 2. 安装依赖包
```bash
pip install xlwings pywin32
```
### 3. 配置 Excel 信任设置
在运行测试前,需要启用 VBA 项目对象模型访问:
1. 打开 Excel
2. 文件 > 选项 > 信任中心
3. 信任中心设置 > 宏设置
4. 勾选"信任对 VBA 工程对象模型的访问"
5. 重启 Excel
## 快速开始
### 1. 创建演示文件
### 1. 使用演示文件测试
项目包含 `demo.xlsm` 演示文件,包含以下测试用例:
首先创建一个包含测试代码的 Excel 文件:
@@ -46,47 +94,78 @@ python vba_test_runner.py demo.xlsm Module1 TestSuccessfulProcedure
python vba_test_runner.py demo.xlsm Module1 TestErrorProcedure TestTypeMismatch TestSubscriptError TestSuccessfulProcedure
```
**全项目自动化测试**(自动发现并测试所有过程):
```bash
python vba_test_runner.py demo.xlsm --all
```
## 输出示例
### 单个测试
**成功时**:
```
[PASS] TestSuccessfulProcedure - Test Passed
[PASS] TestSuccessfulProcedure
```
**失败时**:
**失败时**(包含调用链):
```
[FAIL] TestErrorProcedure - Test Failed
Error Description: Division by zero
Error Line: 30
Source Code: result = x / y
[FAIL] TestErrorProcedure
Error: Division by zero
Location: Module1.TestErrorProcedure:30
Call Chain: Module1.MainProc -> Module1.TestErrorProcedure
Source: result = x / y
```
### 批量测试
```
===== VBA Batch Test Results =====
===== VBA 批量测试结果 =====
[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
[1/4] [FAIL] TestErrorProcedure
Error: Division by zero
Location: Module1.TestErrorProcedure:30
Source: result = x / y
[2/4] [FAIL] TestTypeMismatch
Error: Type mismatch
Location: Module1.TestTypeMismatch:20
Source: y = x
[3/4] [FAIL] TestSubscriptError
Error: Subscript out of range
Location: Module1.TestSubscriptError:40
Source: value = arr(5)
[4/4] [PASS] TestSuccessfulProcedure
===== Test Summary =====
Passed: 1/4
Failed: 3/4
```
### 全项目测试(--all 模式)
```
===== VBA 全项目自动化测试 =====
发现 8 个可测试的入口点
总过程数: 12
[1/8] 测试 Module1.TestErrorProcedure
[FAIL] TestErrorProcedure
Error: Division by zero
Location: Module1.TestErrorProcedure:30
Call Chain: Module1.TestErrorProcedure
Source: result = x / y
[2/8] 测试 Module1.TestSuccessfulProcedure
[PASS] TestSuccessfulProcedure
...
===== Test Summary =====
Passed: 5/8
Failed: 3/8
```
## 工作原理
### 代码编织Code Weaving
@@ -95,8 +174,9 @@ Failed: 3/4
1. **解析 VBA 代码**: 识别所有 Sub/Function 过程
2. **注入行号标签**: 在可执行代码前插入数字标签10, 20, 30...
3. **注入错误处**: 添加 `On Error GoTo` 语句和错误处理块
4. **创建 Source Map**: 维护行号到源代码的映射
3. **注入调用栈管**: 添加 `LogEntry``LogExit` 调用
4. **注入错误处理**: 添加 `On Error GoTo` 语句和错误处理块
5. **创建 Source Map**: 维护行号到源代码的映射
### VBA 行号机制
@@ -108,46 +188,100 @@ VBA 的 `Erl` 函数会返回最近执行的行号标签:
30 result = x / y ' Erl 将返回 30
```
### 调用链追踪
系统使用 `CallStack` 类模块追踪过程调用:
```vba
Sub LogEntry(procName, moduleName)
CallStack.Push procName, moduleName
End Sub
Sub LogExit()
CallStack.Pop
End Sub
Function GetCallChain() As String
GetCallChain = CallStack.GetCallChain() ' 返回 "Module1.Main -> Module1.Helper"
End Function
```
### 测试流程
```
原始 VBA 代码
代码编织器注入行号和错误处理
代码编织器注入行号、调用栈管理和错误处理
注入 _TestLogger 辅助模块
注入 TestLogger 模块和 CallStack 类模块
热替换目标模块代码
执行 VBA 宏
_TestLogger 读取结果
从 TestLogger 读取结果(包含调用链)
格式化输出
```
## 架构设计
### 模块划分
### 核心类
```
vba_test_runner.py
├── TestResult (测试结果数据类)
│ ├── procedure_name - 过程名称
│ ├── success - 测试是否成功
│ ├── error_number - 错误代码
│ ├── error_description - 错误描述
│ ├── error_line - 错误行号
│ ├── source_code - 源代码
│ ├── error_module - 错误发生的模块 (新增)
│ └── call_chain - 完整调用链 (新增)
├── CodeWeaver (代码编织器类)
│ ├── parse_procedures() - 解析 VBA 过程
│ ├── weave_procedure() - 编织单个过程
│ ├── _inject_line_numbers() - 注入行号标签
── _inject_error_handler() - 注入错误处理
│ ├── parse_modules() - 解析所有模块
│ ├── parse_procedures() - 解析 VBA 过程
│ ├── weave_procedure() - 编织单个过程
── weave_procedure_with_callstack() - 编织过程(带调用栈)
│ ├── weave_module_all_procedures() - 编织模块的所有过程
│ ├── weave_all_modules() - 编织所有模块
│ ├── _inject_line_numbers() - 注入行号标签
│ ├── _inject_error_handler() - 注入错误处理
│ └── _inject_error_handler_with_callstack() - 注入错误处理(带调用栈)
├── LoggerInjector (日志模块注入器类)
│ ├── inject_or_replace() - 注入或替换 Logger 模块
── LOGGER_MODULE_CODE - Logger 模块的 VBA 代码
── LOGGER_MODULE_CODE - TestLogger 模块的 VBA 代码
│ └── CALLSTACK_CLASS_CODE - CallStack 类模块的 VBA 代码
└── TestRunner (执行控制器类)
├── run_test() - 执行完整测试流程
├── run_test() - 执行单个测试
├── run_all_tests() - 执行全项目测试 (新增)
├── discover_all_tests() - 发现所有可测试过程 (新增)
├── _get_vba_code() - 读取 VBA 代码
├── _replace_module_code() - 热替换模块代码
├── _execute_macro() - 执行宏
── _get_test_result() - 获取测试结果
── _get_test_result() - 获取测试结果
├── _is_entry_point() - 判断是否为测试入口点 (新增)
├── _weave_all_modules_inplace() - 就地编织所有模块 (新增)
└── _run_single_test() - 执行单个测试(批量用) (新增)
```
### TestResult 结构
```python
@dataclass
class TestResult:
procedure_name: str # 过程名称
success: bool # 测试是否成功
error_number: int = 0 # 错误代码
error_description: str = "" # 错误描述
error_line: int = 0 # 错误行号
source_code: str = "" # 源代码
error_module: str = "" # 错误发生的模块
call_chain: str = "" # 完整调用链 (如: "Module1.A -> Module1.B")
```
## 关键技术点
@@ -160,18 +294,41 @@ vba_test_runner.py
- 跳过注释行和空行
- 跳过现有的 On Error 语句
### 错误处理模板
### 错误处理模板(带调用栈)
```vba
Call TestLogger.LogEntry("{proc_name}", "{module_name}")
On Error GoTo Auto_Err_Handler_{proc_name}
... 原有代码 ...
Call _TestLogger.LogSuccess()
Call TestLogger.LogExit()
Call TestLogger.LogSuccess()
Exit Sub/Function
Auto_Err_Handler_{proc_name}:
Call _TestLogger.LogError("{proc_name}", Err.Number, Err.Description, Erl)
Call TestLogger.LogError("{proc_name}", "{module_name}", Err.Number, Err.Description, Erl)
Call TestLogger.LogExit()
```
### CallStack 类模块
```vba
' CallStack 类维护调用栈
Private m_Stack As Collection
Public Sub Push(procName, moduleName)
m_Stack.Add moduleName & "." & procName
End Sub
Public Sub Pop()
m_Stack.Remove m_Stack.Count
End Sub
Public Function GetCallChain() As String
' 返回 "Module1.A -> Module1.B -> Module2.C"
GetCallChain = Join(parts, " -> ")
End Function
```
### 热替换不保存
@@ -187,7 +344,11 @@ Auto_Err_Handler_{proc_name}:
3. **复杂过程**: 对于非常复杂的过程(包含大量 GoTo 语句),可能需要额外处理
4. **只支持标准模块**: 当前版本不支持类模块和窗体模块
4. **模块类型支持**: 支持标准模块Type 1和类模块Type 2不支持窗体模块Type 3
5. **过程过滤**: 全项目测试模式会自动排除以下过程:
-`Worksheet_``Workbook_``Document_` 开头的事件过程
- Logger 相关过程LogEntry、LogExit、LogError 等)
## 扩展开发
@@ -207,9 +368,89 @@ End Sub
python vba_test_runner.py your_file.xlsm Module1 YourTestProcedure
```
### 使用全项目测试
全项目测试模式会自动发现并测试所有过程:
```bash
python vba_test_runner.py your_file.xlsm --all
```
系统会:
1. 扫描所有标准模块和类模块
2. 识别所有 Sub/Function 过程
3. 过滤掉事件过程和内部方法
4. 为所有模块注入调用栈管理
5. 逐个执行测试并生成报告
### 自定义 Logger 模块
修改 `LoggerInjector.LOGGER_MODULE_CODE` 可以自定义日志记录逻辑。
修改 `LoggerInjector.LOGGER_MODULE_CODE``CALLSTACK_CLASS_CODE` 可以自定义日志记录逻辑。
### 程序化使用
```python
from vba_test_runner import TestRunner
# 单个测试
runner = TestRunner("demo.xlsm", visible=False)
result = runner.run_test("Module1", "TestErrorProcedure")
# 全项目测试
runner = TestRunner("demo.xlsm", visible=False)
results = runner.run_all_tests()
# 发现所有测试
tests = runner.discover_all_tests()
for test in tests:
print(f"{test['module']}.{test['procedure']}")
```
## 开发工具
### VS Code 调试配置
项目包含 `.vscode/launch.json` 调试配置,可直接在 VS Code 中调试:
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: VBA Test Runner",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/vba_test_runner.py",
"console": "integratedTerminal",
"args": ["demo.xlsm", "--all"]
}
]
}
```
使用方法:
1. 在 VS Code 中打开项目
2.`F5` 或点击调试面板
3. 选择 "Python: VBA Test Runner" 配置
4. 可以在 `launch.json` 中修改 `args` 来测试不同的场景
### 查看流程图
详细的技术流程图请查看 [vba_test_runner_flowchart.md](vba_test_runner_flowchart.md),包含:
- 系统架构概览
- 单个测试执行流程
- 全项目测试流程
- 代码编织流程
- 错误处理与调用链追踪
- 类关系图
### 实现总结
查看 [IMPLEMENTATION_SUMMARY.md](IMPLEMENTATION_SUMMARY.md) 了解:
- 项目完成状态
- 已测试的验证场景
- 关键技术实现细节
- 已解决的问题
## 常见问题
@@ -219,12 +460,37 @@ A: 系统使用热替换技术,在内存中修改代码,测试完成后使
**Q: 如何测试类模块中的方法?**
A: 当前版本只支持标准模块。要测试类模块,可以创建一个包装的 Sub 在标准模块中调用类方法
A: 系统支持类模块Type 2的测试。在全项目测试模式下类模块中的公共方法会被自动发现和测试
**Q: 调用链是如何追踪的?**
A: 系统在每个过程入口注入 `LogEntry` 调用,在出口注入 `LogExit` 调用。`CallStack` 类模块维护一个栈结构,记录所有正在执行的过程,错误发生时可以生成完整的调用链。
**Q: 全项目测试模式和手动指定过程有什么区别?**
A: 全项目测试模式(`--all`)会:
- 自动发现所有可测试的过程
- 一次性编织所有模块的代码
- 逐个执行测试并生成汇总报告
- 更适合大规模测试和回归测试
手动指定过程模式更适合:
- 调试单个过程
- 快速验证修复
- 选择性测试某些功能
**Q: 如何在 VS Code 中调试?**
A: 项目包含 `.vscode/launch.json` 配置文件。在 VS Code 中按 `F5` 即可启动调试,可以在配置中修改测试参数。
**Q: 可以捕获运行时警告吗?**
A: 当前版本只捕获错误。要捕获警告,需要修改 Logger 模块来处理 `InfoMessage` 事件。
**Q: 虚拟环境是必须的吗?**
A: 强烈推荐使用虚拟环境来隔离项目依赖。项目包含 `.gitignore` 规则来忽略虚拟环境目录。
## 许可证
MIT License

View 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 无法提供的调试功能。