简介:面向软件测试入门与进阶人员的测试用例范例文档,围绕如何设计结构完整、可执行、可追溯的测试用例展开,适合准备面试、课程作业或项目测试设计时对照参考。压缩包仅含1个doc文件,体积约329KB,内容以方法讲解配合表格化实例呈现,便于直接查阅与改写。已有4782人浏览学习,说明其作为入门范本具有一定参考价值。文档先梳理优秀用例应包含的项目名称、版本、模块、前置条件、编号、操作步骤、测试数据与预期结果等要素,再以B/S登录功能为例给出黑盒测试用例表;随后分别用三角形判定、NextDate函数、线性表排序、自动售货机等场景演示等价类划分、边界值分析、错误推测、因果图与判定表驱动法的设计思路,读者可据此掌握从需求分析到用例落地的完整路径,并积累可复用的测试场景与排错视角。
1. 从一个登录框说起:测试用例到底在解决什么问题
一个登录框摆在面前,用户名叫 test、密码正确、点登录,进去了——这算把登录测完了吗?多数人第一反应是「算吧」。可线上真出问题,出岔子的往往不是这一步:密码连输错三次会怎样、用户名塞进二十多个字符会不会把输入框撑爆、什么都不填直接点登录,提示语到底是哪一句。测试用例干的就是这件事,把那些没被算进去的部分,提前写成白纸黑字的清单。
通俗地说,测试用例是一份写给执行者看的操作说明:前置条件是什么、按哪几步走、填什么数据、最后该看到什么结果。它不追求写得漂亮,追求换个人来跑,结论一模一样。搜「测试用例 实例 例子」的人,多半是想找一份能照着自己填的样板。下面借一份登录功能的经典实例,把「写成什么样」「为什么这么写」「什么时候别这么写」依次说清。
2. 为什么用例要写成一张表:12 项要素里的防翻车设计
见过不少团队的用例,存在聊天记录里、写在便签上、或者浓缩成一句「登录功能已自测通过」。这种记录在当时够用,过两周就变成玄学:谁也说不清当时到底是怎么点的,出了问题也没法复现一遍。一份用例之所以要写成表格,不是因为流程要求,而是因为人的记性撑不到下一个版本。
2.1 用例不是「随手点点」,它是一份可复现的指令单
打个比方,随手点点像朋友在电话里告诉你「出门左拐第二个路口」,你自己能走,换成别人就得打电话再问一遍;写成用例,相当于给对方一张带门牌号的地址条,谁拿着都能走到。
这份「地址条」要满足三件事。可复现,指的是换一台机器、换一个人、隔一个月,照着步骤走能得到同样的过程;可判定,指的是最后必须有一个明确的「该看到什么」,否则通过还是不通过全靠当场感觉;可追溯,指的是出了问题能顺着编号找回需求、找回开发、找回执行时间。三条里缺任何一条,这份用例在缺陷评审会上都说不上话。
原文列的 12 项要素,本质上就是在为这三件事服务:软件或项目名称、内部版本号、功能模块名、用例简述、参考信息、依赖关系、前置条件、用例编号、步骤与测试数据、预期结果与实际结果、开发人员与测试人员、执行日期。看着琐碎,逐条问一句「不写会怎样」,答案都很实在。
2.2 预期结果是整张表的地基
原文在第 10 条后面专门加了一句括号:「预期结果(这是最重要的)」。这句话值得单独拎出来说。一份用例如果没有写清预期结果,它就不是用例,只是一段操作记录——像一场没有评分标准的考试,考完谁也不知道算不算过。
反例很容易构造。假设某个登录失败场景,预期结果只写了「登录失败」四个字。执行的人跑完,页面弹出一个白底的错误码 500,他想了想,确实没登录进去,于是标了通过。等到用户反馈「提示语看不懂」时,回头一看,用例根本拦不住这个问题。而原文 TC6 的写法是「系统登录失败,并提示:请检查用户名和密码的输入是否正确」——精确到文案级别,执行的人就没有自由发挥的余地。
实际结果那一栏,原文说「如果有 BUG 管理工具,这条可以省略」,道理也简单:缺陷单里已经记了实际现象和截图,用例表里再抄一遍属于重复劳动。但如果团队还在用表格管理缺陷,这一栏就得留着,不然「预期」和「实际」对不上时,没人说得清差在哪。
2.3 定位、执行、追溯:12 项要素其实是三组
把 12 项要素按用途归类,会比一条条背下来好记。
| 组别 | 包含字段 | 解决的问题 |
|---|---|---|
| 定位组 | 软件或项目名称、内部版本号、功能模块名、用例编号 | 这条用例测的是谁、哪个版本、哪个模块 |
| 执行组 | 前置条件、依赖关系、步骤与测试数据、预期结果 | 怎么跑、跑之前要满足什么、跑完该看到什么 |
| 追溯组 | 参考信息、开发人员、测试人员、执行日期 | 出问题找谁、什么时候跑的、依据是哪份需求 |
版本号这一项最容易被省,也最容易出事。同一个登录模块,内部版本 2.3.1 和 2.3.2 对「连续三次失败」的处理可能完全不同,用例上不写版本,等于把结论悬在半空,谁引用谁倒霉。
用例编号的规则原文给了一个简单模板:软件名称简写-功能块简写-NO.,比如LOGIN-TC-006。别小看这个编号,它是缺陷单、需求文档、自动化脚本之间唯一的连接点。编号乱,追溯链就断。
2.4 一份可以直接照抄的用例骨架
把上面三组装进一份结构化描述里,大概长这样。
# 用例骨架,字段对应原文 12 项要素,可映射到任意缺陷管理工具 case_id: LOGIN-TC-006 # 编号:软件简写-模块简写-序号 project: 某业务系统 # 软件或项目名称 version: 内部版本 2.3.1 # 内部版本号,必须写死 module: 登录模块 # 功能模块名 title: 用户名与密码均为空时点击登录 # 用例简述 depends_on: [] # 依赖的其他用例,本例无 precondition: 已打开登录页,浏览器版本固定 # 前置条件 steps: - action: 不填写用户名 - action: 不填写密码 - action: 单击登录按钮 expected: 登录失败,提示"请检查用户名和密码的输入是否正确" # 最重要的一栏 actual: "" # 有缺陷管理工具时可留空 owner_dev: 开发负责人 # 出问题找谁 owner_qa: 测试负责人 executed_at: "" # 执行日期这段结构本身不含逻辑,作用是强迫写用例的人把每一栏都填满。depends_on为空和写成[LOGIN-TC-002, LOGIN-TC-004],差别很大——后者说明这条用例必须在指定用例跑过之后才有意义,乱序执行会得到假结果。precondition里写死浏览器版本,是为了排除渲染差异带来的干扰,否则同一个断言在不同环境下时过时不过,排查成本高得离谱。
3. 黑盒测试用例长什么样:登录功能那张表逐行拆开看
理论说完,落到实例上。原文给的是一个 B/S 结构的登录功能,明确标注为黑盒测试用例——不关心后台怎么校验、代码怎么写的,只看输入了什么、屏幕上出现了什么。
3.1 先把被测对象钉死:结构、方法与运行环境
黑盒这两个字经常被误解成「随便点点」。它真正的含义是:测试者站在系统外部,只能通过界面和接口观察行为,判断依据是需求规格而不是源码。好处是贴近真实用户视角,坏处是覆盖是否充分全靠设计功底。
所以被测对象的描述必须钉死到可复现的程度:B/S 结构意味着通过浏览器访问,不涉及客户端安装;浏览器版本被写死(原文举例为 IE6.0 SP4),因为老版本浏览器的输入框长度限制、脚本行为和新版本差别不小,不写死环境,同一份用例在不同机器上结论可能相反。
功能描述给了四条:在地址栏输入地址后显示登录界面;输入用户名和密码后系统自动校验并给出提示;用户名或密码任一未输入时给出提示;连续 3 次未通过验证时自动关闭浏览器。这四句话是用例的源头,后面每一条用例都应该能对回其中一句,对不上的要么是漏了需求,要么是需求本身没写清。
3.2 TC1 到 TC4:界面基线、输入成功与长度容错
TC1 是初始页面显示,从入口进入后要求「页面元素完整,显示与详细设计一致」。这条用例常被嫌多余,实际很有价值:登录页少一个输入框、按钮被样式挤到屏幕外,后面的用例全部白跑。它相当于给整条用例链做一次基线校验。
TC2 输入已存在的用户 test,预期「输入成功」。注意这里只验证输入动作本身,还没有点登录,属于把大流程拆成小步骤的写法——一步一断言,失败时能立刻定位是哪一环出的问题。
TC3 是这批用例里最有意思的一条:输入aaaaabbbbbcccccdddddeeeee,预期结果是「输入到蓝色显示的字符时,系统拒绝输入」,备注写着「输入数据超过规定长度范围」。它验证的不是「能不能输进去」,而是「上限在哪、超了之后系统怎么表现」。是直接截断、是弹出提示、还是默默吞掉多余字符,三种行为对用户来说体验完全不同,必须由预期结果说清。
TC4 输入与用户名关联的密码 test,预期输入成功。它和 TC2 的关系是「配对」:一个管账号框,一个管密码框,两个框的输入规则不一定一样(密码框通常还会遮显示),所以不能合并成一条。
3.3 TC5 到 TC10:成功路径之后才是重点
真正的覆盖度体现在失败路径上。原文从 TC5 到 TC10 这六条,把登录能出的岔子基本扫了一遍。
| 用例 | 场景 | 操作要点 | 预期结果 |
|---|---|---|---|
| TC5 | 登录成功 | 在 TC2、TC4 基础上单击登录 | 登录系统成功 |
| TC6 | 用户名密码都为空 | 不填任何内容直接点登录 | 登录失败,提示「请检查用户名和密码的输入是否正确」 |
| TC7 | 只缺密码 | 填用户名,不填密码 | 登录失败,提示「需要输入密码」 |
| TC8 | 密码错误 | 用户名正确,密码与用户名不匹配 | 登录失败,提示「错误的密码」 |
| TC9 | 用户名不存在 | 输入系统中没有的用户名和密码 | 登录失败,提示「用户名不存在」 |
| TC10 | 安全校验 | 连续 3 次未成功 | 提示「您没有使用该系统的权限,请与管理员联系!」 |
这六条的设计思路值得琢磨。TC6 和 TC7 看似重复,其实区分了「两个都空」和「只缺一个」,因为不少系统的校验是先判整体再判单项,两个都空时走的可能是另一条分支。TC8 和 TC9 同理,一个是密码错,一个是账号不存在,提示语不同,说明后台走的是两套判断逻辑。
提示:TC9 的提示语「用户名不存在」其实透露了账号是否存在这一信息。有的产品会故意把它和密码错误统一成一句模糊提示,就是为了不告诉对方这个账号到底存不存在。这一栏写什么,取决于需求怎么定,测试的人别自作主张改文案。
还有个细节值得单独拎出来:功能描述里写的是「连续 3 次未通过验证时,自动关闭浏览器」,而 TC10 的预期结果写的是给出一句权限提示。两处对不上。这不是用例写错了,恰恰是用例的价值所在——需求和用例一旦打架,说明中间有一块没人说清楚,这时候该做的是去找需求方确认,而不是挑一个看着顺眼的写上。
3.4 依赖关系不是摆设:TC5 为什么要等 TC2 和 TC4
TC5 的测试步骤写的是「TC2,TC4,单击登录按钮」。这句话的含义是:TC5 不是独立可执行的,它建立在 TC2 和 TC4 已经完成的前提上——用户名框里已经有 test,密码框里已经有对应的密码,此时点登录才有意义。
这就是第 2 章里「依赖关系」那一栏的现实用法。如果执行顺序被打乱,先跑 TC5,两个框都是空的,点登录必然失败,执行的人一看结果是失败,可能就判这条用例挂了,实际上是用例顺序错了。
依赖关系还决定了一件事:当 TC2 失败时,TC5 应该标记为「阻塞」而不是「失败」。两者的区别在缺陷统计里很大,前者说明还没轮到它上场,后者才是真问题。很多团队把这两种状态混着用,最后缺陷率算出来忽高忽低,复盘时谁也说不清。
4. 等价类划分与边界值分析:把无穷多种输入砍成几堆
登录这种表单,输入组合几乎是无穷的。用户名可以是任意字符串,密码可以是任意长度,全测一遍不可能。等价类和边界值解决的就是这个问题:不去覆盖每一个值,而是覆盖每一「类」值,再在类的接缝处多扎几针。
4.1 三角形问题:先列条件,再分有效与无效
原文用的是一个非常经典的例子:程序读入三个整数 a、b、c 作为三角形三条边,判断它是一般三角形、等腰三角形还是等边三角形。
这类题目的难点不在代码,在于输入和输出之间的关系比较绕。原文先把隐含的输入要求一条条挖出来:必须是整数、必须三个数、必须非零、必须为正数、两边之和大于第三边、等腰、等边,一共七条。挖条件这一步最考验功底,因为「正整数」这种要求,需求文档里经常只写「输入边长」,剩下的全靠推。
条件列完,等价类就分成两拨。满足全部七条的输入属于有效等价类,代表「合法输入」;只要违反其中一条就落入某个无效等价类,代表「非法输入」。有效类用来验证功能对不对,无效类用来验证系统扛不扛得住乱填。
4.2 五条用例覆盖七类:有效等价类表怎么读
原文给出的有效等价类用例只有五条:
| 用例 | a | b | c | 覆盖的等价类 |
|---|---|---|---|---|
| 1 | 3 | 4 | 5 | 条件 1 至 7 |
| 2 | 4 | 4 | 5 | 条件 1 至 7,外加等腰 |
| 3 | 4 | 5 | 5 | 条件 1 至 7,外加等腰 |
| 4 | 5 | 4 | 5 | 条件 1 至 7,外加等腰 |
| 5 | 4 | 4 | 4 | 条件 1 至 7,外加等边 |
前四条覆盖一般三角形和等腰三角形,第五条覆盖等边。第 2、3、4 条看着像重复,其实是在验证「任意两条边相等」这个条件是否被正确识别——如果代码只判断了 a 等于 b 这一种情况,第 3 条和第 4 条就会露馅。
这就是等价类划分的精髓:同一类里挑一个代表,但「类」要划得足够细。等腰三角形如果只测一条,代码里可能只写了一个等号判断,剩下两种排列组合就漏了。
原文在有效用例之后紧接着写「覆盖无效等价类的测试用例」,但没有列出具体数据。这一块的原则是:每一个无效等价类至少要有一条用例去踩,而且最好一次只踩一个坑。比如同时输入负数和非整数,程序报错时你分不清是哪个条件起了作用,用例的定位能力就废了。
4.3 NextDate 的边界值:为什么专挑 1911、1912、2050、2051
等价类划分管「类别」,边界值分析管「接缝」。程序在区间内部通常表现平稳,真正容易出错的是边界那一圈——循环少跑一次、判断用大于还是大于等于,问题全在这里冒出来。
原文的 NextDate 函数是个典型:输入月、日、年,输出下一天的日期。隐含范围是月份 1 到 12、日期 1 到 31、年份 1912 到 2050。
| 用例 | 月 | 日 | 年 | 预期输出 | 考察点 |
|---|---|---|---|---|---|
| Test1 | 6 | 15 | 1911 | 1911.6.16 | 年份下界外侧 |
| Test2 | 6 | 15 | 1912 | 1912.6.16 | 年份下界 |
| Test3 | 6 | 15 | 1913 | 1913.6.16 | 年份下界内侧 |
| Test4 | 6 | 15 | 1975 | 1975.6.16 | 区间中部 |
| Test5 | 6 | 15 | 2049 | 2049.6.16 | 年份上界内侧 |
| Test6 | 6 | 15 | 2050 | 2050.6.16 | 年份上界 |
| Test7 | 6 | 15 | 2051 | 2051.6.16 | 年份上界外侧 |
| Test8 | 6 | -1 | 2001 | day 超出范围 | 日期下界外侧 |
| Test9 | 6 | 1 | 2001 | 2001.6.2 | 日期下界 |
| Test10 | 6 | 2 | 2001 | 2001.6.3 | 日期下界内侧 |
| Test11 | 6 | 30 | 2001 | 2001.7.1 | 日期上界内侧 |
| Test12 | 6 | 31 | 2001 | 输入日期超界 | 月份实际天数 |
| Test13 | 6 | 32 | 2001 | day 超出范围 | 日期上界外侧 |
| Test14 | -1 | 15 | 2001 | 月份超出范围 | 月份下界外侧 |
| Test15 | 1 | 15 | 2001 | 2001.1.16 | 月份下界 |
| Test16 | 2 | 15 | 2001 | 2001.2.16 | 月份下界内侧 |
| Test17 | 11 | 15 | 2001 | 2001.11.16 | 月份上界内侧 |
| Test18 | 12 | 15 | 2001 | 2001.12.16 | 月份上界 |
| Test19 | 13 | 15 | 2001 | 月份超出范围 | 月份上界外侧 |
这张表里有两处特别值得看。
第一处是 Test12。日等于 31,明明落在 1 到 31 这个合法区间内,预期结果却是「输入日期超界」。原因很简单:6 月只有 30 天。也就是说,一个字段的边界不完全由它自己的取值范围决定,还要受业务规则约束。只看「1 到 31」就写用例,这类问题永远测不出来。
第二处是 Test7。年份 2051 已经超出了 1912 到 2050 的范围,按理该被拒绝,预期输出却写的是 2051.6.16,说明这个上界拦截在实际程序里可能并没生效。这正是边界值用例最想抓到的东西——不是在验证程序对不对,而是在逼程序表态。
4.4 用几行代码把边界值批量列出来
边界值用例的数量随字段数线性增长,手写容易漏。原文这张表就是三段式取值:下界外侧、下界、下界内侧、上界内侧、上界、上界外侧。
def boundary_values(low, high): """返回一个区间的六个典型取值""" return [low - 1, low, low + 1, high - 1, high, high + 1] ranges = {"month": (1, 12), "day": (1, 31), "year": (1912, 2050)} for field, (low, high) in ranges.items(): print(field, boundary_values(low, high)) # month [0, 1, 2, 11, 12, 13] # day [0, 1, 2, 30, 31, 32] # year [1911, 1912, 1913, 2049, 2050, 2051]low - 1和high + 1是离点,也就是刚出界的那两个值,专门用来验证拦截有没有做;low和high是上点,边界本身;low + 1和high - 1是内点,用来确认区间内部没被误伤。原文那张表在日字段上还额外测了 -1,属于比标准三段式更保守的取法,多测一个不吃亏。
要提醒的是,这段代码只负责生成候选值,不负责判断结果。像 6 月 31 日这种「区间内但业务上非法」的情况,代码算不出来,还是得靠人把业务规则补进去。
5. 因果图、判定表与错误推测法:从拍脑袋到列清楚
等价类和边界值擅长处理单字段、单条件的输入。可现实里很多功能是「几个条件组合起来决定一个结果」,比如投币买饮料、比如按优先级派单。这类场景光靠分类不够,得把条件和结果的对应关系摊开来看。
5.1 错误推测法:排序程序最该测的五个输入
错误推测法听起来最不像方法——凭经验猜哪里会错。但猜也是有套路的:盯着那些「程序作者容易想当然」的地方下手。
原文给了一个排序程序的清单,列了五种特别值得测的输入:线性表为空;表中只有一个元素;输入已经排好序;输入按逆序排好;部分或全部元素相同。
这五条几乎每一条都对应一个经典 bug。空表会踩到循环边界,一不小心就下标越界;单元素表会让某些交换逻辑空转;已排序的输入能测出「优化分支」有没有写错;逆序输入考验交换次数和性能;元素全同则是判断「大于」还是「大于等于」的试金石,用错了排序结果看着对,实际元素顺序被打乱。
这份清单的价值不在穷举,在于它可迁移。凡是涉及「集合处理」的功能,都可以照着问一遍:空的时候呢、只有一个的时候呢、已经有序的时候呢、完全反着来的时候呢、全都一样的时候呢。四个问题问下来,能捞出一批隐藏问题。
5.2 因果图:自动售货机的 5 个原因、5 个结果、4 个中间结点
原文的自动售货机例子,规格说明是这样的:投入 5 角或 1 元硬币,按下橙汁或啤酒按钮,相应饮料送出;如果没有零钱找,「零钱找完」红灯亮,此时投入 1 元并按下按钮,饮料不送出且 1 元退还;如果有零钱找,红灯灭,送出饮料的同时退还 5 角。
先列原因:有零钱找、投入 1 元、投入 5 角、按下橙汁按钮、按下啤酒按钮。再列结果:红灯亮、退还 1 元、退还 5 角、送出橙汁、送出啤酒。原文还设置了四个中间结点,用来表示处理过程中的中间状态:投入 1 元且按下饮料按钮、按下橙汁或啤酒按钮、应当找 5 角且售货机有零钱、钱已付清。
中间结点是因果图里最容易被跳过、也最不该跳过的部分。原因和结果之间往往不是直接连线,而是先合成一个中间状态,再由中间状态触发结果。「应当找 5 角」这件事,前提是「顾客付的是 1 元」加上「售货机有零钱」,这两件事同时成立才谈得上找零,缺一个结果就完全不同。
原因之间还有约束关系。投入 1 元和投入 5 角显然不会同时发生,这种「不可能同时成立」在因果图里叫互斥约束。类似的还有包含、唯一、要求、屏蔽几类,不同教材的叫法略有差异,作用都是把「哪些组合根本不存在」这件事显式写出来,避免后面生成一堆无意义用例。
因果图真正的产出不是那张图,而是图转成的判定表。图只是中间产物,画完就该丢掉。
5.3 判定表:3 个条件 8 条规则,怎么化简成 5 条
原文的判定表例子是一套设备维修派单规则:功率大于 50 马力、维修记录不全、已运行 10 年以上,满足其中任一条件就优先维修。
三个条件,每个两种取值,组合数是 2 的 3 次方,八条规则。原文第一步就是数规则个数,这一步不能省——条件一多就容易漏列。然后把条件桩(三个条件)和动作桩(优先处理、其他处理)列出来,逐行填条件项,从最后一行开始往上填,Y N Y N Y N Y N这样交替,填完八列正好不重不漏。
初始判定表长这样:
| 规则 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 功率大于 50 马力 | Y | Y | Y | Y | N | N | N | N |
| 维修记录不全 | Y | Y | N | N | Y | Y | N | N |
| 运行超过 10 年 | Y | N | Y | N | Y | N | Y | N |
| 优先处理 | √ | √ | √ | √ | √ | √ | √ | |
| 其他处理 | √ |
八条规则里,前七条的动作完全相同,都是优先处理。这时候就该化简了。化简的规则很朴素:如果两条规则只有一个条件的取值不同、动作却一样,说明这个条件对结果没有影响,可以把它标成「-」,两条合成一条。
按这个口径走一遍:功率为 Y 的四条,后两个条件怎么变都优先,合并成一条;功率为 N 但维修记录不全的两条,运行年限不影响结果,合并成一条;功率为 N、维修记录也不全、但运行超过 10 年的,单独一条;三个条件全为 N 的,走其他处理。八条压到四条。
| 规则 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 功率大于 50 马力 | Y | N | N | N |
| 维修记录不全 | - | Y | N | N |
| 运行超过 10 年 | - | - | Y | N |
| 优先处理 | √ | √ | √ | |
| 其他处理 | √ |
「-」不是「随便填」,而是「这一格的取值不影响结果」。写进用例时,这一格对应的是多条具体数据,执行的人得自己补全。这一点经常被误解成「这一条不用测」,结果化简完之后覆盖度反而掉了。
6. 易混概念与使用边界:五个容易混的说法,和三个别硬套的场景
方法学到这里,最容易出问题的不是不会用,而是用串了。下面五组概念,是初学者提问频率最高的。
6.1 等价类划分与边界值分析
易混点:两者都在「挑代表值」,看起来是同一件事。
为什么混:它们经常成对出现,教材里也常放在同一章,用同一个三角形例子讲。
怎么区分:等价类管「类别」,把输入空间切成有效和无效两堆,每堆挑一个代表;边界值管「接缝」,专挑区间两端和紧贴两端外侧的值。一个验证「这类输入处理得对不对」,一个验证「临界那一格有没有差一」。只做等价类,区间内部的逻辑覆盖到了,边界上的差一错误照样漏;只做边界值,非边界的非法输入又没人管。两个一起上才完整。
6.2 因果图与判定表
易混点:都用来处理多条件组合,产出物看着也像。
为什么混:因果图最后要转成判定表,两张表描述的是同一套规则。
怎么区分:因果图是过程工具,
本文还有配套的精品资源,点击获取