ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

黑盒测试实战:以人民币大写转换为例掌握等价类边界值与pytest参数化

黑盒测试实战:以人民币大写转换为例掌握等价类边界值与pytest参数化 简介一份面向软件测试初学者的黑盒测试实验报告围绕人民币数字大写转换功能展开完整展示从测试策略选择、用例设计到结果分析的实践流程。报告基于Windows 7与Visual Studio 10.0环境核心内容包括等价类划分、边界值分析和因果图三类黑盒测试方法的具体应用每个模块均附有测试用例设计与异常记录适合学习软件测试、准备实验报告或复习黑盒测试技术的学生参考。资源为单个PDF文档压缩包约417KB内容精炼、目录清晰目前已有427人学习。通过该报告可快速理解黑盒测试的等价类划分规则、边界值选取技巧及因果图逻辑组合方式并借鉴其测试记录与总结的写法对完成类似实验或课程作业具有直接启发。1. 人民币数字大写转换用黑盒测试从一个函数开始撬动整个测试思维人民币数字大写转换是软件测试入门阶段很经典的一个实验题目输入一串数字比如123.45期望输出壹佰贰拾叁元肆角伍分。这类函数没有复杂的界面、没有数据库业务规则却足够多刚好能把黑盒测试里常用的等价类划分、边界值分析、错误推测和决策表全部练一遍。很多高校甚至把它直接放在软件测试实验一中用来摸底因为学生不需要先理解接口内部实现只要拿到需求就能写出完整测试用例。黑盒测试的重点在于把“被测对象”当做一个黑盒子输入什么、输出什么、规则是什么全部来自需求文档。正因为它不依赖代码所以毕业后进入软件测试项目实战时这套思路仍然成立。面试时面试官也常问“黑盒测试有哪些方法”这篇文章不会停在概念上而是从需求分析一路走到 pytest 脚本、边界组合和缺陷报告让你拿到一个新的测试任务时能立刻照着做。2. 黑盒测试需求分析人民币大写转换的等价类、边界值与错误推测在做任何黑盒测试之前第一件事不是写用例而是把被测函数的外部契约定下来。没有契约你连一个有效用例都写不出来。2.1 先定契约被测函数的外部接口我一般会先写一页简单的需求说明哪怕实验题没有给你完整文档也要自己把关键规则列出来。假设被测函数是convert_amount()它的接口签名是def convert_amount(amount: str) - str: 把字符串形式的金额转换为中文大写比如 123.45 - 壹佰贰拾叁元肆角伍分 在这个实验里可以约定输入是普通十进制数字字符串支持两位小数合法范围是0.01到999999999999.99超过这个范围属于无效输入输出必须使用人民币大写字符零壹贰叁肆伍陆柒捌玖、拾佰仟万亿、元角分整整数部分为 0 时可以不输出“零元”例如0.05输出为伍分小数部分有角无分时末尾加“整”例如1.10输出壹元壹角整无效输入抛出ValueError或者返回统一的错误提示。把契约写清楚黑盒测试用例就可以直接从这些规则里推导。很多测试新人拿到题目就开始乱填用例然后抱怨不知道期望结果是什么本质上是契约没定好。2.2 等价类划分从需求里圈出有效和无效输入等价类划分是最容易上手的黑盒测试方法。核心思想是把所有输入分成若干“同类”的集合只要从每个集合里取一个有代表性的值就等于覆盖了这一整类情况。下面是我在实验报告里常用的一张等价类表等价类类型输入特点代表性输入期望行为有效-整数元小数部分为.00100.00输出以“元整”结尾有效-有角无分小数部分只有角位1.10输出以“角整”结尾有效-有角有分角和分都存在1.23输出包含“角”和“分”有效-无角有分角位为 0分位不为 01.05输出包含“零伍分”有效-含连续零中间有多个 01001.00输出必须只出现一次“零”有效-万级金额超过一万12345.67输出包含“万”有效-亿级金额超过一亿123456789.01输出包含“亿”无效-负数数字前有--100.00抛出异常或返回错误码无效-空字符串空输入抛出异常或返回错误码无效-字符包含英文字母12ab.00抛出异常或返回错误码无效-位数过多小数超过两位1.001抛出异常或返回错误码无效-超上限超过约定范围1000000000000.00抛出异常或返回错误码这张表的价值在于它不依赖具体实现直接反映用户和业务对函数的约束。实际做实验时把这 12 类用例全部变成可执行用例黑盒测试的覆盖基础就已经很扎实了。2.3 边界值分析数字类输入最容易翻车的点等价类只能保证覆盖了某一类输入但最容易出 bug 的往往是类与类之间的“边界”。比如“有小数”和“没有小数”的边界就是1.00“万级”和“千级”的边界是10000.00。我通常会在边界值这一栏把左右两侧的值都列出来边界位置边界左值边界右值为什么值得测最小金额0.010.02金额合法范围的下界角分进位0.09/0.100.11分位向角位进位元角进位0.99/1.001.01角分向元进位拾元边界9.99/10.0010.01十位数字切换万位边界9999.99/10000.0010000.01是否出现“万”单位亿位边界99999999.99/100000000.00100000000.01是否出现“亿”单位上限边界999999999999.98/999999999999.99无需求约定的最大值这些边界值看起来单调但它们恰好对应人民币大写转换里“进位”和“单位切换”两类最容易出错的逻辑。黑盒测试中永远不要嫌边界值多因为开发在写if amount 10000这类条件时差一个等号就会在边界处翻车。2.4 错误推测哪些输入你会直觉地想去试等价类和边界值是“基于规范的测试”错误推测则是“基于经验的测试”。对这个函数来说即使需求里没说我也会凭直觉补充下面这些输入前导零00100.00有些实现会下意识地按字符串长度截断结果输出错误全角数字.输入法切换后容易触发编码字符问题科学计数法1e3表面看起来是数字但不能作为合法的金额输入多个小数点1.2.3典型的异常输出带空格123.45接口是否支持自动 trim小数位全零123.000超过两位但实际上没有金额差异。错误推测没有固定公式但它能让你的用例集显得更像“真实业务场景”。在实验报告里把这些用例单列一节老师一般会认为你有测试敏感性。把这些输入加入测试脚本后还能顺便验证异常处理是否合理而不是直接把栈信息抛给用户。3. 将人民币大写转换黑盒测试用例固化为 pytest 参数化脚本手写用例表只是第一步真正跑测试还是要靠脚本。黑盒测试并不排斥自动化反而是最适合自动化的场景因为只需要准备输入和期望输出然后断言实际结果是否一致。3.1 准备测试目录和依赖pytest 基本结构先建一个干净的测试工程避免和被测代码混合到一起mkdir amount_blackbox cd amount_blackbox python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install pytest创建被测模块所在目录amount_app并在其中放一个空白的__init__.py。被测对象是外部提供的convert_amount测试脚本只从它对外暴露的模块导入不看内部实现。目录结构大致是amount_blackbox/ ├── amount_app/ │ ├── __init__.py │ └── converter.py # 被测函数可能来自实验代码 ├── tests/ │ ├── test_amount_cases.py │ └── cases.csv └── pytest.ini这个结构的好处是把测试代码和业务代码分开后续跑 CI 或批量回归都不会互相干扰。3.2 用参数化直接表达用例矩阵pytest 的pytest.mark.parametrize可以把一份用例列表变成多个独立测试用例。这是黑盒测试最常用的方式。# tests/test_amount_cases.py import pytest from amount_app.converter import convert_amount valid_cases [ (0.01, 壹分), (0.10, 壹角整), (0.35, 叁角伍分), (1.00, 壹元整), (1.11, 壹元壹角壹分), (10.50, 壹拾元伍角整), (1001.00, 壹仟零壹元整), ] pytest.mark.parametrize(amount,expected, valid_cases) def test_valid_amount_conversion(amount, expected): assert convert_amount(amount) expected这里的parametrize会把valid_cases里每组元组拆成参数amount和expected每生成一个测试用例。比如第一组会变成test_valid_amount_conversion[0.01-壹分]失败时标签里就带着输入值一眼能看到是哪组数据挂了。还需要写一组无效输入用例invalid_cases [ -1.00, , 12ab.00, 1.001, 1000000000000.00, 1e3, ] pytest.mark.parametrize(amount, invalid_cases) def test_invalid_amount_raises(amount): with pytest.raises(ValueError): convert_amount(amount)这样就把第 2 章里的无效等价类也固化成代码了。3.3 用 CSV 文件把测试数据和脚本分离直接在代码里写用例列表很方便但如果实验要求上交的用例表有 50 条以上我一般会改成从 CSV 读取数据。这样测试脚本不用频繁改数据文件可以单独维护也更贴近真实工作里测试人员维护测试数据的习惯。type,amount,expected valid,0.01,壹分 valid,0.10,壹角整 valid,1.11,壹元壹角壹分 invalid,-1.00, invalid,12ab.00,读取脚本如下import csv import pytest from amount_app.converter import convert_amount def load_cases(path): valid_cases [] invalid_cases [] with open(path, newline, encodingutf-8) as f: for row in csv.DictReader(f): if row[type] valid: valid_cases.append((row[amount], row[expected])) else: invalid_cases.append(row[amount]) return valid_cases, invalid_cases valid_cases, invalid_cases load_cases(cases.csv) pytest.mark.parametrize(amount,expected, valid_cases) def test_valid_cases_from_csv(amount, expected): assert convert_amount(amount) expected pytest.mark.parametrize(amount, invalid_cases) def test_invalid_cases_from_csv(amount): with pytest.raises(ValueError): convert_amount(amount)这里的load_cases在模块导入时执行测试收集阶段就能看到数据。如果 CSV 写了多余的空行DictReader会自动跳过。注意文件路径是相对pytest运行根目录的建议在pytest.ini里配置好根目录避免不同环境下路径不一致。3.4 运行、收集测试并生成可阅读报告用例写好后用一条命令运行全部测试pytest -v --tbshort --maxfail1-v会列出每条用例的执行结果--tbshort让失败信息尽量简短--maxfail1表示失败一个就停节省排查时间。常用的几个参数可以记成一张表参数作用-v打印详细测试名称和结果--maxfail1遇到第一个失败即停止--lf只重跑上次失败的用例--junitxmlreport.xml生成 JUnit 格式报告方便 CI 解析-k invalid按 키 词筛选要执行的用例生成 JUnit 报告的命令pytest --junitxmltest_report.xml这份 XML 可以被 Jenkins、GitLab CI 直接读取实验报告里也可以直接贴测试结果截图。运行后如果出现红色失败不要急着改用例先回去核对是“用例期望写错了”还是“被测函数真的有问题”这两个结论的后续动作完全不同。4. 被黑盒测试经常忽略的转换细节零、角分和整位的决策组合人民币大写转换真正复杂的地方不在“123.45”这种普通数字而在于 0 的出现位置和小数部分组合。很多测试用例设计者会在等价类和边界值之后停手结果漏掉最常见的连续零规则。4.1 解析大写转换规则中的“零”粘贴位置中文大写金额对连续的零有一套约定。以元为单位时一个不为零的数字后面如果有零一般只写一个“零”不能重复输出多个“零”。例如输入期望输出要检查的重点1000.00壹仟元整千位后连续三个零不能写成“零佰零拾零元”1001.00壹仟零壹元整中间只需一个“零”1010.00壹仟零壹拾元整零和拾共存的写法100001.00壹拾万零壹元整万位和个位之间隔了零100000000.01壹亿元零壹分元部分为 0但分位不为 0 时如何衔接这些用例几乎 90% 的转换实现都会在某个场景上出错。测试时不能只测一个“1001”要把 0 出现在个位、十位、百位、跨单位位的情况都覆盖到。4.2 决策表覆盖角分零整组合光测整数部分的零还不够角分和整位的组合也需要系统化。这里可以用决策表把条件列出来再为每种条件组合生成一条用例。规则编号元部分是否为 0角位是否为 0分位是否为 0输入示例核心预期R1否是是1.00输出包含“元整”R2否是否1.01角位为零应补“零壹分”R3否否是1.10输出包含“角整”R4否否否1.11输出包含“角分”R5是是是0.00按需求约定输出如“零元整”R6是是否0.01输出不需要“零元”直接“壹分”R7是否是0.10输出“壹角整”R8是否否0.11输出“壹角壹分”决策表的好处是保证每个组合都被测到尤其是 R2 这种角位为零的分位非零场景很多人会漏掉。把这 8 条转化为 pytest 用例后可以再补一个更自动化的组合验证函数def test_map_between_groups(): for yuan in [0, 1, 10]: for jiao in [0, 1, 5]: for fen in [0, 1, 9]: amount f{yuan}.{jiao}{fen} result convert_amount(amount) # 黑盒层面对输出做基础结构校验 assert result assert 元 in result or 角 in result or 分 in result这段代码不是断言具体输出而是检查每一组输入都至少能返回一个包含金额单位的结果避免转换函数遇到某些组合时直接返回空字符串或抛出未捕获异常。4.3 一个防呆断言方法快速校验输出而不是纯手工看遇到大量组合用例时一个很实用的技巧是写一个“输出规则校验函数”在精确断言之外做二次防御。比如断言结果里不能有连续重复的“零”不能出现“零元整”这种不符合通用习惯的字符串也不能在单位顺序上出错def assert_chinese_amount_morphology(amount, actual): assert actual assert 零零 not in actual assert not in actual if amount.endswith(.00): assert actual.endswith(整)这不算黑盒测试的终点但它能帮你快速筛掉一批明显异常的输出再人工复核剩下少数用例。把这句话放到测试夹具里跑实验报告里的样本会从容很多。5. 从黑盒测试执行到缺陷报告人民币大写转换实验的收尾与进阶测试执行到最后重要的不是“跑了多少条用例”而是“这些用例到底能不能说明被测函数是否通过验收”。5.1 测试执行结果怎么判定才算通过对转换函数来说判定条件必须严格实际输出字符串必须和期望完全一致。不要因为“只差一个整字”就认为接近通过因为用户不会接受“壹拾元伍角”这种缺了整字的金额。在 pytest 里字符串比较本身就是完全匹配但我建议执行前再检查一遍用例的期望值是否和需求一致。特别要注意全角冒号、中文逗号这类肉眼容易忽略的字符。5.2 一份缺陷报告至少包含哪些字段如果测试中发现了问题比如输入1001.00时实际输出变成壹仟壹元整应该按下面的模板记录缺陷字段内容标题convert_amount 对 1001.00 输出缺少“零”前置条件被测版本 0.1Python 3.10输入1001.00期望结果壹仟零壹元整实际结果壹仟壹元整复现步骤1. 调用convert_amount(1001.00)2. 打印返回值严重程度高优先级P1这种结构化报告比直接在群里丢一张截图有效得多面试时讲软件测试流程也可以拿它当案例。5.3 回归测试技巧用快照对比防止再次翻车实验做到第二轮时最容易出现的场景是“这次改动把上次正确的数字改坏了”。我一般会在最后一轮加一个快照回归把全量用例的期望输出保存成一个文本文件或 JSON跑测试时逐条对比pytest --lf --tbshort --maxfail5 --junitxmlresult.xml--lf只重跑上次失败的用例适合快速判断缺陷是否真的被修复如果要从零完整回归直接去掉--lf再跑一次。此时再看测试报告里的绿色通过数才能真正给这次软件测试实验下一个“通过”或者“不通过”的结论。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进