
简介面向软件测试课程学习者这是一份中科大软件测试实验一关于人民币数字大写转换功能的黑盒测试实验报告。报告以Windows 7与Visual Studio 10.0为环境系统阐述了等价类划分、边界值和因果图三类典型黑盒测试技术的实际应用包含引言、引用文件、测试结果概述、详细测试用例设计、测试记录、评价与总结等完整结构。包内为单个PDF文件体积约417KB便于阅读和打印。目前已有427人学习使用适合正在完成软件测试实验作业或希望掌握黑盒测试用例设计方法的读者参考。报告除详细记录各测试类型的输入、预期输出与实际结果外还给出了软件整体评估、测试环境对结果的影响分析及针对性改进建议能够帮助读者理解黑盒测试的完整流程并可直接借鉴其报告结构与测试设计思路。1. 人民币数字大写转换的黑盒测试先看这份实验报告踩过的坑数字转人民币大写这类程序业务逻辑不复杂真正复杂的是“零”的处理规则什么时候写零、什么时候不写、什么时候补“整”。如果用白盒测试对着源码逐行看反而容易陷入分支迷宫里。这份中科大软件测试实验报告选择的是纯黑盒路线在 Windows 7 上用 Visual Studio 2010 的命令行工具去测一个队友写的 test1.exe覆盖了等价类划分、边界值分析、因果图三种方法最终暴露出三个典型缺陷非法字符输入导致死循环、超过分位的数据不按四舍五入处理、界面停留在命令行。这些坑在真实财务系统里对应的就是脏数据崩溃和金额精度错账不是简单的课程作业问题。下面我按这份报告的实际流程把每一类测试怎么设计、参数怎么设、结果怎么判定以及缺陷怎么复现和修补完整拆开讲一遍。适合正在做软件测试课程设计、准备软件测试面试、或者接手过金额转换类需求的人对照着实践。2. 等价类划分输入域怎么切才能用 12 条用例覆盖全场景等价类划分的核心思路是把无穷的输入域切成一堆有限的“子集”从每个子集里抽一个代表值做测试。关键在于子集切得准不准切得太粗会漏掉边界切得太细用例数就爆炸。这份报告针对人民币大写转换程序把输入域按“是否有效、数字格式、金额范围、精度位数、零的出现位置”切成了若干等价类。2.1 有效与无效等价类的切分依据报告里用到的测试用例表表 4.2虽然排版有些乱但可以还原出它的切分逻辑等价类代表输入预期结果实际结果不输入数据空提示并不执行Pass非法字符sss提示并不执行Fail死循环多个小数点1..1提示并不执行Pass超大数据超过千亿提示并不执行Pass负值-1提示并不执行Pass高精度数据1002.345按两位精度处理Warn未四舍五入连续零1002.03人民币壹仟零贰元零叁分Pass无零数据12345.67人民币壹万贰仟叁佰肆拾伍元陆角柒分Pass角位为0分位非01.01人民币壹元零壹分Pass前几位为000123人民币壹佰贰拾叁元整Pass到元为止1.00人民币壹元整Pass注意一个细节报告把“输入数据到元为止”和“角位分位都为0”合并成了 1-12 用例实际上这两类在转换规则上等价——都是小数点后全零输出要补“整”。我建议拆开分别测 1.00 和 1因为“是否显式输入小数点”在解析逻辑里经常是两个不同的分支。2.2 用什么命令跑测试用例原报告是基于 Windows 7 和 Visual Studio 2010 的 x64 兼容命令提示符。当时的做法很简单test1.exe 是命令行程序直接把输入作为参数传进去test1.exe 1002.03 test1.exe 12345.67 test1.exe -1 test1.exe sss对于不输入数据的情形直接不带参数启动程序应该输出提示信息然后退出。这里有个容易忽略的点Windows 的命令提示符默认代码页是 GBK如果程序输出的是 UTF-8 编码的汉字控制台会显示乱码。我通常会在测试前先执行一次chcp 65001切到 UTF-8 代码页再跑被测程序否则“实际结果”这一列没法准确判断。参数说明test1.exe是被测可执行文件后面跟一个参数就是要转换的金额字符串。程序内部应当先做合法性校验再做转换。如果程序支持交互式输入则需要通过管道输入echo 1002.03 | test1.exe死循环的复现路径正是第三种方式输入sss后程序可能在解析字符时没有处理非数字的 else 分支导致循环变量不递增。这个我在第 4 章会专门讲复现和定位方法。2.3 等价类用例的判定标准等价类的测试判定不能只看“有没有输出”要看三件事输出前缀是否为“人民币”字样、大写金额是否紧接前缀无空白、末尾的“整/正”字是否按规则出现。报告中的预期结果列写得比较随意比如 1-6 高精度数据只写了“人民币壹仟零贰元叁”实际按规则 1002.345 应该取角分分位后多余位数不四舍五入的话得到的就是 1002.34 对应的“壹仟零贰元叁角肆分”。但报告记录为 Warn说明测试执行人已经意识到这是程序缺陷而不是简单地用预期结果去比对。这里的经验是等价类用例不仅要覆盖正反两个方向还要在预期结果里标注“缺陷”还是“错误”。“缺陷”指程序行为与需求规格不一致但程序本身没崩溃“错误”指程序崩溃、死循环、无响应。报告里 1-6 是 Warn缺陷1-2 是 Fail错误这种区分在测试报告里非常专业也方便后续研发按优先级修复。3. 边界值分析与因果图找出临界点和组合条件里的隐藏 Bug等价类划分解决了“代表性”问题但边界值最容易出错的地方恰恰是等价类边缘。这份报告用 Max value、Max value1、Min value、Min value-1 设计了四组边界测试再用因果图补上了多条件组合场景。两套方法配合起来才算把输入域真正摸透。3.1 边界值用例的设计参数用例编号边界类型输入期望结果实际结果2-1Max value999999999999.99人民币玖仟玖佰玖拾玖亿玖仟玖佰玖拾玖万玖仟玖佰玖拾玖元玖角玖分Pass2-2Max value11000 亿即 1000000000000.00提示并不执行Pass2-3Min value0.00人民币零元整Pass2-4Min value-1-0.01提示并不执行Pass注意这里的 Max value 是 999999999999.99也就是千亿减一分。程序说明里“数字大小不应超过千亿”所以边界值要取到千亿整以及千亿减 0.01。报告里 2-1 输入写的是 Max value 100.00这是排版错误实际输入应为 999999999999.99。边界值方法有一个容易被忽视的要求除了输入边界还要测输出边界。人民币大写输出的边界是“零”和“整”的出现位置。例如 100000000000.00整数部分中间有连续的零输出应该是“人民币壹仟亿元整”这里的“亿”后面是否要补“零”就需要专门验证。3.2 因果图的约束条件建模因果图法在需求有多个输入条件组合时比排列组合更高效。报告把输入条件原因列成了五条① 输入不超过转换最大值的整数② 输入至小数点后一位③ 输入至小数点后两位④ 输入数字中间含有零⑤ 非法输入输出结果结果是a. 输出 xx 元整b. 输出至角位c. 输出至分位d. 输出结果含有零e. 错误提示这里有一个关键约束条件①与②③不能同时成立因为“整数”意味着没有小数点条件②与③不能同时成立因为小数位数不可能同时是 1 位和 2 位条件⑤与其他条件互斥。把这些约束画成因果图再转成判定表每一行就是一个测试用例。报告的表 4.6 已经生成了一张判定表用例编号3-1 原因①②⑤0④0 结果a 输入 7 用例编号3-2 原因①②⑤0④1 结果d 输入 107 用例编号3-3 原因①⑤0②④1 结果b 输入 7.1 用例编号3-4 原因①⑤0②④1 结果bd 输入 1007.1 用例编号3-5 原因①⑤0③④0 结果c 输入 7.12 用例编号3-6 原因①⑤0③④1 结果cd 输入 1007.12 用例编号3-7 原因⑤1其他均为0 结果e 输入 sss可以看出这组用例把“整数/一位小数/两位小数”与“含零/不含零”做了交叉覆盖等价于测试了转换逻辑中“零”字插入的所有条件分支。3.3 因果图用例的自动化执行脚本原报告是在命令行手工输入参数但我在复现时会写一个简单的批处理脚本把判定表里的用例批量跑完并对比预期输出echo off setlocal enabledelayedexpansion set cases7 107 7.1 1007.1 7.12 1007.12 sss for %%i in (%cases%) do ( echo [case %%i] test1.exe %%i echo exitcode!errorlevel! ) pause这个脚本的核心逻辑是遍历所有用例输入每跑一个用例就打印输入值和程序退出码。实际使用时我会在程序输出后面手动加一行“预期结果”再统一 diff。如果程序支持退出码约定0 表示成功非 0 表示错误那么errorlevel也可以作为判定依据。但报告中的 test1.exe 明显没有做退出码约定所以只能靠捕获输出文本比对。对于死循环的 sss 用例脚本会卡住这时需要在任务管理器里杀掉 test1.exe 进程或者给批处理加一个超时机制timeout /t 5 nul taskkill /f /im test1.exe 2nul注意这里timeout /t 5的意思是等待 5 秒后强制结束 test1.exe。这是命令行黑盒测试遇到死循环时最实用的兜底手段否则整个测试管道会被一个坏用例卡死。4. 死循环与精度缺陷复现路径、原因定位和修复建议这份报告最有价值的部分不是用例设计而是对三个缺陷的记录和分析。尤其是非法字符导致死循环这类问题在纯黑盒测试中很难直接定位但通过控制变量法可以缩小范围。下面我把复现路径和修复思路展开来说。4.1 非法字符死循环的复现与定位死循环的复现步骤非常简单启动 test1.exe输入sss程序没有输出任何错误信息光标一直闪烁无法继续输入。从黑盒角度观察程序卡在了字符解析循环里。我猜测常见的 C 语言实现是这样的// 简化版解析输入字符串中的每个字符 char *p input; while (*p ! \0) { if (*p 0 *p 9) { process_digit(*p); p; // 只有数字才前进 } else if (*p .) { process_dot(); p; } // 缺少 else 分支遇到非数字非点字符时p 不前进循环永不退出 }这段代码的问题在于else分支缺失。当*p是s时既不是数字也不是小数点p指针不移动循环条件永远不满足于是陷入死循环。定位这种问题最直接的办法是在关键循环处加调试输出或者用调试器打断点观察p的值。但黑盒测试环境没有源码报告里只记录了现象没有定位到具体行号这很正常。如果是我处理我会在测试报告中附上一个最小复现用例和进程堆栈截图方便开发快速定位。4.2 高精度数据不四舍五入的缺陷边界第二个缺陷是输入1002.345时输出是“人民币壹仟零贰元叁”而不是按四舍五入处理成“壹仟零贰元叁角伍分”。这说明程序在解析小数位时可能只取了前两位数字第三位直接丢弃或者用了整型截断。C 语言中浮点转整型是直接截断的比如(int)(1002.345 * 100)因为浮点数精度问题可能得到100234而不是100235。这类缺陷的测试重点在于厘清需求原需求里只说了“程序对精度大于分位的数据进行处理时没有进行四舍五入”没有明确说必须要四舍五入。但从人民币大写转换的财务语义看分位后多余的位数必须四舍五入到分否则账目不平。修复建议是用字符串方式解析小数部分而不是用浮点数// 修复思路取小数点后的子串判断第三位是否 5 char *dot strchr(input, .); if (dot ! NULL) { int len strlen(dot 1); if (len 2) { if (dot[3] 5) { // 第三位 5 时对前两位加 1并考虑进位到元 add_one_to_cents(); } dot[3] \0; // 截断到两位 } }注意这种修复要处理“99.999”的进位场景分位加一后变成 100 分需要进位到元元位加一角分位归零。这是人民币转换程序里最容易写错的地方比大数的亿万亿转换更容易踩坑。4.3 无图形化界面的测试成本报告中提到程序未实现图形化用命令行测试时“稍有不便”。这个缺陷其实不算 Bug但对软件质量有实际影响。我在测试命令行程序时会写一个 Python 脚本做输入输出比对这样不用每次手动敲命令还能自动生成测试报告import subprocess cases [ (999999999999.99, 人民币玖仟玖佰玖拾玖亿玖仟玖佰玖拾玖万玖仟玖佰玖拾玖元玖角玖分), (1002.345, WARN), # 已知缺陷实际输出低于预期 (sss, DEADLOOP), ] for inp, expected in cases: try: proc subprocess.run( [test1.exe, inp], capture_outputTrue, textTrue, timeout3 ) output proc.stdout.strip() status PASS if output expected else FAIL print(f{inp}: {status} - {output}) except subprocess.TimeoutExpired: print(f{inp}: FAIL (deadloop))这个脚本的思路是每个用例放到一个子进程里跑设置 3 秒超时超时了就判定为死循环。这里timeout3表示最多等待 3 秒超过就杀死进程。自动化测试的价值在于把黑盒测试从“手工点按钮”升级成“可回归的资产”以后再改动程序只需重跑一遍脚本就能知道哪些功能被破坏了。5. 用中文金额语法树验证转换正确性最后分享一个在这个实验基础上可以立刻用起来的技巧不用枚举大量用例而是按中文大写的语法结构构造验证规则用规则去检查程序输出。这样比手工比对几百条用例高效得多也能发现输出里的细微错误。人民币大写字符串有一个稳定的语法模式人民币 [数词 亿] [数词 万] [数词 元] [角 分] [整]。其中“数词”由零、壹到玖和拾佰仟组成但“零”的出现有特殊限制。我写了一个简单的 Python 验证函数def validate_rmb_upper(s): # s 形如人民币壹仟零贰元零叁分 if not s.startswith(人民币): return False body s[3:] if 亿元 in body: yi, rest body.split(亿元, 1) if not all(c in 零壹贰叁肆伍陆柒捌玖拾佰仟 for c in yi): return False body rest if 万元 in body: wan, rest body.split(万元, 1) if not all(c in 零壹贰叁肆伍陆柒捌玖拾佰仟 for c in wan): return False body rest if body.endswith(整): body body[:-1] if 元 in body: yuan, rest body.split(元, 1) if not all(c in 零壹贰叁肆伍陆柒捌玖拾佰仟 for c in yuan): return False body rest if body : return True # 剩余部分只允许角分不允许出现“零”以外的分隔异常 return all(c in 零壹贰叁肆伍陆柒捌玖角分 for c in body) # 验证所有历史测试输出 outputs [人民币壹仟零贰元零叁分, 人民币壹万贰仟叁佰肆拾伍元陆角柒分] for out in outputs: print(out, validate_rmb_upper(out))这段代码的核心是“按额度单位切分”的思想。“亿元”和“万元”都是必须成对出现的分隔词切分后每一段都应该是合法的千位以内数字表达。比如“壹仟零贰”合法“壹仟零”不合法因为末尾不能是零。执行结果会打印两个 True但如果程序输出“人民币壹仟零贰元整”这里的“整”前面有“元”且“元”前面是“贰”按规则“到元为止写整”是合法的验证函数也能正确处理。把这个验证函数接在批量测试脚本后面就可以对任意数量的输出做自动断言不再依赖人工阅读汉字。对于测试报告里的每个用例你还能把输入金额和输出字符串一起存入表格形成一张“输入-输出-语法校验”三维映射表。这样即使被测程序不提供日志你也能从输出字符串的结构异常反推出输入解析的错误位置。这套方法在代码评审、回归测试、以及你将来面试软件测试岗位谈项目时都是比“我写了几十条用例”更有说服力的实践细节。本文还有配套的精品资源点击获取