From 15e9fabf251b2cd34af8e5e71c6367fcc6af77ee Mon Sep 17 00:00:00 2001 From: Misaka Date: Wed, 4 Feb 2026 23:37:10 +0800 Subject: [PATCH] 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 --- README.md | 352 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 309 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 898bebc..a5210b3 100644 --- a/README.md +++ b/README.md @@ -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