软件测试与质量保证 - 软件测试方法 邓松 daonicool@sina.com
方法论和具体方法 从方法论看,更多体现了一种哲学的思想,例如辩证统一的方法,在测试中有许多对立统一体,如静态测试和动态测试、白盒测试和黑盒测试、自动化测试和手工测试等。 软件测试的方法论来源于软件工程的方法论,例如有面向对象的开发方法,就有面向对象的测试方法。
黑盒子和白盒子 结构测试 逻辑驱动测试 客户需求 事件驱动 输入 输出 功能测试 数据驱动测试
静态的和动态的 主持人 作者 记录员 列席人员 内审员 技术专业人员 用户代表 不正式 正式 互审 走读 审查会议 运行程序
自动测试和手工测试 手工模拟用户操作
白盒测试方法 1 语句覆盖 2 判定覆盖 3 条件覆盖 4 判定条件覆盖 5 条件组合覆盖 6 路径覆盖 7 基本路径测试法
白盒测试方法 逻辑覆盖:以程序的内部逻辑结构为基础,分为语句覆盖、判定覆盖、判定-条件覆盖、条件组合覆盖等 基本路径测试:在程序控制流程的基础上,分析控制构造的环路复杂性,导出基本可执行路径集合,从而设计测试用例。
语句覆盖 语句覆盖法的基本思想是设计若干测试用例,运行被测程序,使程序中的每个可执行语句至少被执行一次 如果是顺序结构,就是让测试从头执行到尾 如果有分支、条件和循环,需要利用下面的方法,执行足够的测试覆盖全部语句
判定覆盖 判定覆盖法的基本思想是设计若干用例,运行被测程序,使得程序中每个判断的取真分支和取假分支至少经历一次,即判断真假值均曾被满足。 一个判定往往代表着程序的一个分支, 所以判定覆盖也被称为分支覆盖。
分支测试 - example 1 PROGRAM som ( maxint, N : INT ) 2 INT result := 0 ; i := 0 ; 3 IF N < 0 4 THEN N := - N ; 5 WHILE ( i < N ) AND ( result <= maxint ) 6 DO i := i + 1 ; 7 result := result + i ; 8 OD; 9 IF result <= maxint 10 THEN OUTPUT ( result ) 11 ELSE OUTPUT ( “too large” ) 12 END.
分支测试 - continued 测试覆盖全部语句: maxint N 但没有覆盖所有分支; 用例采取: 则覆盖全部分支 10 -1 i:=i+1; result:=result+i; (i<N) and (result<=maxint) result<=maxint N < 0 N := -N; output(result); output(too large); exit start yes no result=0 i=0 测试覆盖全部语句: maxint N 10 -1 0 -1 但没有覆盖所有分支; (N>=0) 用例采取: maxint N 10 3 0 -1 则覆盖全部分支
(i<N) and (result<=maxint) 条件覆盖 条件覆盖的基本思想是设计若干测试用例,执行被测程序以后,要使每个判断中每个条件的可能取值至少满足一次。 (i<N) and (result<=maxint) True False i<N True False result<=maxint
条件测试 目标: 保证测试到每个逻辑条件取值为 true 和 false 简单条件: (a rel-op b) where rel-op={<, ≤, =, ≠, ≥, >} (和 NOT连用), 即 a≤b; NOT(a≤b) 组合条件: 由 AND, OR连接的多个简单条件,即(a>b) AND (c<d) 关系表达式: (E1 rel-op E2) 其中 E1 和 E2 是算术表达式,即 ((a*b+c)>(a+b+c)) 测试发现的错误: 布尔操作符 – 关系操作符 布尔变量 – 算术表达式 布尔括弧
条件测试 (2) 分支测试 组合条件C, 测试C为 true和 false分支、以及每个条件 例如 C = (a>b) AND (c<d) 测试: a>b TRUE, FALSE c<d TRUE, FALSE C TRUE, FALSE True AND True T. And F., F. And T., F. And F. 域测试 表达式 E1 rel-op E2, 测试 E1 ≧ ≦ = < > ≠ E2 如果E1 和 E2 正确,发现rel-op的错误 发现E1 或 E2的错误,全面分析它们的不同 如果某表达式含有n个变量,则 需要进行2n 测试
条件测试 的示例 但没有覆盖判定(decision) 条件覆盖不等于判定覆盖 ( i = result = 0 ) : i:=i+1; result:=result+i; (i<N) and (result<=maxint) result<=maxint N < 0 N := -N; output(result); output(too large); exit start yes no ( i = result = 0 ) : maxint N i<N result<=maxint -1 1 true false 1 0 false true 测试了所有条件 但没有覆盖判定(decision) 条件覆盖不等于判定覆盖
判定条件覆盖 判定-条件覆盖是判定和条件覆盖设计方法的交集,即设计足够的测试用例,使得判断条件中的所有条件可能取值至少执行一次,同时,所有判断的可能结果至少执行一次 测试用例 取值条 件 具体取值条件 判定条 件 通过路径 输入:a=2,b=1,c=6 输出:a=2,b=1,c=5 T1,T2, T3,T4 a>0, b>0, a>1, c>1 M=.T. N=.T. P1(1-2- 4) 输入:a=-1,b=-2,c=-3 输出:a=-1,b=-2,c=-5 F1,F2, F3,F4 a<=0, b<=0, a<=1, c<=1 M=.F. N=.F. P4(1-3- 5)
条件组合测试 条件组合覆盖的基本思想是设计足够的测试用例,使得判断中每个条件的所有可能至少出现一次,并且每个判断本身的判定结果也至少出现一次。 它与条件覆盖的差别是它不是简单地要求每个条件都出现“真”与“假”两种结果,而是要求让这些结果的所有可能组合都至少出现一次
示例 (1) 组合编号 覆盖条件取值 判定条件取 值 判定-条件组合 1 T1,T2 M=.T. a>0,b>0,M取真 2 T1,F2 M=.F. a>0,b<=0,M取假 3 F1,T2 a<=0,b>0,M取假 4 F1,F2 a<=0,b<=0,M取假 5 T3,T4 N=.T. a>1,c>1,N取真 6 T3,F4 a>1,c<=1,N取真 7 F3,T4 a<=1,c>1,N取真 8 F3,F4 N=.F. a<=1,c<=1,N取假
示例 (2) 覆盖了所有组合,但覆盖路径有限,1-2-5 没被覆盖 测试用例 覆盖条件 覆盖路径 覆盖组合 输入:a=2,b=1,c=6 T1,T2,T3, T4 P1(1-2-4) 1,5 输入:a=2,b=-1,c=-2 输出:a=2,b=-1,c=-2 T1,F2,T3, F4 P3(1-3-4) 2,6 输入:a=-1,b=2,c=3 输出:a=-1,b=2,c=6 F1,T2,F3, T4 3,7 输入:a=-1,b=-2,c=-3 输出:a=-1,b=-2,c=-5 F1,F2,F3, F4 P4(1-3-5) 4,8 覆盖了所有组合,但覆盖路径有限,1-2-5 没被覆盖
路径测试 顾名思义,路径覆盖就是设计所有的测试用例,来覆盖程序中的所有可能的执行路径。 测试用例 覆盖路径 覆盖条件 覆盖组合 输入:a=2,b=1,c=6 输出:a=2,b=1,c=5 P1(1-2-4) T1,T2,T3,T4 1,5 输入:a=1,b=1,c=-3 输出:a=1,b=1,c=-2 P2(1-2-5) T1,T2,F3,F4 1,8 输入:a=2,b=-1,c=-2 输出:a=2,b=-1,c=-2 P3(1-3-4) T1,F2,T3,F4 2,6 输入:a=-1,b=2,c=3 输出:a=-1,b=2,c=6 F1,T2,F3,T4 3,7 输入:a=-1,b=-2,c=-3 输出:a=-1,b=-2,c=-5 P4(1-3-5) F1,F2,F3,F4 4,8
基本路径测试 依据代码绘制流程图 确定流程图的圈复杂度(cyclomatic complexity ) 确定线性独立路径的基本集合( basis set ) 设计测试用例覆盖每条基本路径
示例 – 源代码 Procedure: process records 1. Do While records remain 2. Read record; 3. If record field 1 = 0 Then 4. store in buffer; 5. increment counter; 6. Else If record field 2 = 0 Then 7. reset counter; 8. Else store in file; 9. End If 10. End If 11. End Do End
示例 – 流程图 1 2 3 6 4 8 7 9 5 10 11 V(G) = 4
基本路径测试:流程图简化 1 2,3 8 7 6 9 4,5 10 11
流程图的圈复杂度 圈复杂度(Cyclomatic complexity):代码逻辑复杂度的 度量,提供了被测代码的路径数量。复杂度越高,出错的概率越大 V(G) modules 2. Determine the cyclomatic complexity of the resultant flow graph Note: can be determined without developing a flow graph count all conditional statements in a component compound conditions count as 2 (number of Boolean operators + 2) V(G) = 区域数量(由节点、连线包围的区域,包括图形外部区域) V(G) = 连线数量 - 节点数量 + 2 V(G) = 简单可预测节点数量 + 1
流程图复杂度-例子 V(G)=4 Region 1 Region 4 Region 2 1 2,3 7 8 6 9 4,5 10 11
确定线性独立的路径集合 基本集: 由独立路径构成的集合 由基本集导出的测试用例,保证每行代码语句至少被执行一次 基本集合不一定唯一 独立路径: 至少引入一系列新的处理语句或条件的任何路径 基本集: 由独立路径构成的集合 由基本集导出的测试用例,保证每行代码语句至少被执行一次 基本集合不一定唯一 3. Determine a basis set of linearly independent paths equal to cyclomatic complexity number identify predicate nodes as an aid in derivation of test cases
示例:基本路径测试用例 Path1: 1-2-3-6-7-9- 10-1-11 Path2: 1-2-3-6-8- 9-10-1-11 4 5 8 7 3 6 Path1: 1-2-3-6-7-9- 10-1-11 Path2: 1-2-3-6-8- 9-10-1-11 Path3: 1-2-3-4-5- 10-1-11 Path4: 1-11
测试用例覆盖集合中每条路径 基本路径测试并不是测试所有路径的组合,仅仅保证每条基本路径被执行一次 不需要活动图, 但最好绘制程序流程图 计算每个逻辑测试,也就是布尔操作符数加1 最好每个单元都进行基本路径测试,对关键组件则是必要的 4. Prepare test cases that will force execution of each path in the basis set each test case is executed and compared to the expected result this process can be mechanized 基本路径测试并不是测试所有路径的组合,仅仅保证每条基本路径被执行一次
循环测试- 1 1.简单循环(迭代次数n) 目标: 在循环内部及边界上执行测试 完全跳过循环 只经过循环一次 经过循环两次 经过循环m( m < n )次 分别经过循环n-1, n, n+1 次
循环测试- 2 2. 嵌套(Nested)循环 在最里面的循环完成前面所述的简单循环测试,同时设定外部循环的最小迭代次数 逐步向外循环进行 直到所有循环被测试
循环测试- 3 3. 串行连接的循环 独立循环 可以分别看着简单循环测试 依赖性循环 可以看着是嵌套循环 其它非结构循环 重新设计!
黑盒测试方法 等价类划分法 边界值分析法 判定表方法 因果图法 正交试验法 功能图法 错误推测法
等价类划分方法 将程序可能的输入数据分成若干个子集,从每个子集选取一个代表性的数据作为测试用例,等价类是某个输入域的子集,在该子集中每个输入数据的作用是等效的 分为有效等价类和无效等价类。有效等价类是有意义的、合理的输入数据,可检查程序是否实现了规格说明中所规定的功能和性能。无效等价类与有效等价类的意义相反 在分析需求规格说明的基础上划分等价类,列出等价类表 all inputs i1 i4 i2 i3 设计测试用例时,要同时考虑这两种等价类。因为软件不仅要能接收合理的数据,也要能经受意外的考验。
确定等价类的方法 在输入条件规定了取值范围或值的个数的情况下,则可以确立一个有效等价类和两个无效等价类 greater than range less than range in range greater than value less than value value
确定等价类的方法(2) 在输入条件规定了输入值的集合或者规定了“必须如何”的条件的情况下,可以确立一个有效等价类和一个无效等价类。 在输入条件是一个布尔量的情况下,可确定一个有效等价类和一个无效等价类 not member of set member of set Non-Boolean Boolean
确定等价类的方式 (3) 在规定了输入数据的一组值(假定n个),并且程序要对每一个输入值分别处理的情况下,可确立n个有效等价类和一个无效等价类。 在规定了输入数据必须遵守的规则的情况下,可确立一个有效等价类(符合规则)和若干个无效等价类(从不同角度违反规则)。 个人月收入- x 税率 x <=1600 0% 1600< x <2100 5% 500 < = x < 3600 10% 3600 < = x < 6600 15% 6600 < = x < 21600 20% 21600 < = x < 41600 25% … … >101600 45% 个人月收入- x 税率 x <=1600 0% 1600< x <2100 5% 500 < = x < 3600 10% 3600 < = x < 6600 15% 6600 < = x < 21600 20% 21600 < = x < 41600 25% … … >101600 45%
等价类测试用例-Example 等价类1: Integer 等价类2: Decimal fraction 等价类3: Negative 等价类4: Invalid input
根据等价类创建测试用例的步骤 建立等价类表,列出所有划分出的等价类: 为每个等价类规定一个唯一的编号; 设计一个新的测试用例,使其尽可能多地覆盖尚未覆盖的有效等价类 重复c),最后使得所有有效等价类均被测试用例所覆盖; 设计一个新的测试用例,使其只覆盖一个无效等价类。 重复e)使所有无效等价类均被覆盖。 输入条件 有效等价类 无效等价类 …
边界值分析方法 程序的很多错误发生在输入或输出范围的边界上,因此针对各种边界情况设置测试用例,可以发现不少程序缺陷。 BVA – Boundary Value Analysis 设计方法: 确定边界情况(输入或输出等价类的边界) 选取正好等于、刚刚大于或刚刚小于边界值作为测试数据
确定边界值的方法 如果输入条件规定了值的范围,则应取刚达到这个范围的边界的值,以及刚刚超越这个范围边界的值作为测试输入数据。 如果输入条件规定了值的个数,则用最大个数、最小个数、比最小个数少一、比最大个数多一的数作为测试数据。 a b a b
确定边界值的方法(2) 如果程序的规格说明给出的输入域或输出域是有序集合,则应选取集合的第一个元素和最后一个元素作为测试用例。 如果程序中使用了一个内部数据结构,则应当选择这个内部数据结构的边界上的值作为测试用例。 Test cases for ABS(x) : class x < 0, arbitrary value: x = -10 class x >= 0, arbitrary value x = 100 classes x < 0, x >= 0, on boundary : x = 0 classes x < 0, x >= 0, below and above: x = -1, x = 1
BVA 示例2 测试 限制性用户输入:6位正整数 无效值: X8 = 000123 正常值(有效类): X1 = 123123 无效类的值: X7 = asdasd 其它? 无效值: X8 = 000123 X9 = asd123 X10 = Empty
BVA 示例3 任意的正常值: 随机选择几个选项 边界值: 选择所有选项 边界值: 一个都不选 边界值: 选择一个选项 Test cases : 任意的正常值: 随机选择几个选项 边界值: 选择所有选项 边界值: 一个都不选 边界值: 选择一个选项
二进制 Term 取值范围 Bit Nibble Byte Word Kilo Mega Giga Tera 0 or 1 0 和 1, byte 由8 bits 构成, 字由4 bytes构成, … Term 取值范围 Bit Nibble Byte Word Kilo Mega Giga Tera 0 or 1 0-15 <Half byte> 0-255 0-65535 or 0-4294967295 1024 1048576 1073741824 1099511627776
ASCII Table Character ASCII Value Null Space / 1 2 9 ; @ A 32 47 48 49 1 2 9 ; @ A 32 47 48 49 50 57 58 64 65 B Y Z [ ‘ a b y z { 66 89 90 91 96 97 98 121 122 123
字符编辑域 Default Empty Blank Null Zero None
一些特殊的边界值 First/last, First-1/Last+1 Min/Max,Min-1/max+1 Star/Finish, Start-1/Finish+1 Empty/Full Less than empty/ more than full Slower/Faster Largest/Smallest Over/Under, just Over/Just Under Shortest/Longest … … 数值 字符 位置 数量 速度 体积
判定表方法 在实际应用中,许多输入是由多个因素构成,而不是单一因素,这时就需要多因素组合分析。 对于多因素,有时可以直接对输入条件进行组合设计,不需要进行因果分析,即直接采用判定表方法。 一个判定表由“条件和活动”两部分组成,也就是列出了一个测试活动执行所需的条件组合,所有可能的条件组合定义了一系列的选择,而测试活动需要考虑每一个选择。
判定表元素 条件桩,列出问题的所有条件 动作桩:列出可能针对问题所采取的操作 条件项:针对所列条件的具体赋值 动作项:列出在条件项(各种取值)组合情况下应该采取的动作。 规则:任何一个条件组合的特定取值及其相应要执行的操作。
判定表方法步骤 列出所有的条件桩和动作桩; 填入条件项; 填入动作项,制定初始判定表; 简化、合并相似规则或者相同动作 示例 见表3-13和3-14
判定表方法步骤
因果图法 多种输入条件的组合,产生多种结果设计测试用例。 设计方法: 分析软件规格说明文档描述的哪些是原因(输入条件),哪些是结果(输出条件),给每个原因和结果赋予一个标示符。 找出原因与结果,原因与原因之间的对应关系,划出因果图 在因果图上标上哪些不可能发生的因果关系,表明约束或限制条件 根据因果图,创建判定表,将复杂的逻辑关系和多种条件组合很具体明确的表示出来 把判定表的每一行作为依据设计测试用例。
因果图法
因果图法
因果图法
因果图法
测试组合会变得很多,如果按照传统的测试方法,会导致很大的测试工作量 为什么要采用正交试验法? 在许多应用系统的测试工作中,不会象判断三角形那样简单,输入条件的因素很多,而且每个因素也不能简单用“是”和“否”来回答。 打印范围分:全部、当前幻灯片、给定范围 打印内容分:幻灯片、讲义、备注页、大纲视图 打印颜色/灰度分: 彩色、灰度、黑白 打印效果分:幻灯片加框和幻灯片不加框。 测试组合会变得很多,如果按照传统的测试方法,会导致很大的测试工作量
正交实验法 确定影响功能的因子与状态 选择一个合适的正交表 利用正交表构造测试数据集 参考 http://www.math.hkbu.edu.hk/UniformDesign http://www.research.att.com/~njas
正交实验法
实例 信息系统中,员工信息查询功能是常见的。例如,设有3个独立的查询条件,以获得特定员工的个人信息 员工号(ID)。 员工姓名(Name)。 员工邮件地址(Mail Address)
功能图法 功能图法就是为了解决动态说明问题的一种测试用例的设计方法 每个程序的功能通常由静态说明和动态说明组成 静态说明描述了输入条件和输出条件之间的对应关系 动态说明描述了输入数据的次序或者转移的次序。 功能图法就是为了解决动态说明问题的一种测试用例的设计方法 功能图由状态迁移图(state transition diagram,STD)和逻辑功能模型(logic function model, LFM)构成
状态迁移图 状态迁移图,描述系统状态变化的动态信息——动态说明,由状态和迁移来描述,状态指出数据输入的位置(或时间),而迁移则指明状态的改变
如何设计测试用例? 功能图法设计测试用例,就是如何覆盖软件所表现出来的所有状态,可以转化为两个层次的测试用例 从功能逻辑模型(决策表或因果图)导出局部测试用例,覆盖各个状态的各种输入数据的组合 从状态迁移图导出整体的测试用例,以覆盖系统(程序)控制的逻辑路径 功能图法是综合运用黑盒方法和白盒方法来设计测试用例,即整体上选用白盒方法——路径覆盖、分支和条件覆盖等,而局部上选用的是黑盒方法——决策表或因果图方法
错误推测法 基于经验和直觉推测程序中所有可能存在的各种错误,从而有针对性地设计测试用例。 发现程序经常出现的错误的方法: 单元测试中发现的模块错误; 产品的以前版本曾经发现的错误; 输入数据为0或字符为空; 当软件要求输入时(比如在文本框中),不是没有输入正确的信息,而是根本没有输入任何内容,单单按了Enter键;
静态测试和动态测试 将需求和设计的评审也纳入测试的范畴,可以看作是广义测试 静态测试包括对软件产品的需求和设计规格说明书的评审、对程序代码的复审等 静态分析的查错和分析功能是其他方法所不能替代的,可以采用人工检测和计算机辅助静态分析手段进行检测 动态测试是通过真正运行程序发现错误,通过观察代码运行过程,来获取系统信息,对系统行为进行验证。
主动测试和被动测试 主动测试方法:测试人员主动向被测试对象发送请求、或借助数据、事件驱动被测试对象的行为,从而验证被测试对象的反应或输出结果 被动测试方法:测试人员不干预产品的运行,而是被动地监控产品在实际环境中运行,通过一定的被动机制来获得系统运行的数据,包括输入、输出数据.
形式化测试方法 1 形式化方法 2 形式化验证 3 基于模型的软件测试 4 扩展有限状态机方法
形式化方法 形式化方法实际上就是基于数学的方法来描述目标软件系统属性的一种技术 凡是采用严格的数学语言、具有精确的数学语义的方法,都称为形式化方法。形式化规范说明语言,一般由3个主要的成分构成:语法、语义和一组关系 形式化方法更大作用是体现在软件规格和验证之上,这包括软件系统的精确建模和软件规格特性的具体描述,即可以看作是面向模型的形式化方法和面向属性的形式化方法
形式化的具体方法 基于模型的方法,如Z语言、B语言等 代数方法,如OBJ、CLEAR、ASL、ACT等 过程代数方法,如CSP、CCS、ACP、LOTOS、TPCCS等 基于逻辑的方法,如区间时序逻)、Hoare 逻辑、模态逻辑、时序逻辑、时序代理模型等。 基于网络的方法
形式化验证 形式化验证,就是根据某些形式规范或属性,使用形式逻辑方法证明其正确性或非正确性。 一般通过形式化规范进行分析和推理,研究它的各种静态和动态性质,验证是否一致、完整,从而找出所存在的错误和缺陷。 无法证明某个系统没有缺陷,因为不能定义 “没有缺陷”。只能证明一个系统不存在我们可以想得到的缺陷,以及验证满足系统质量要求的属性
形式化验证的一些具体方法 有限状态机(FSM)或扩展有限状态机(EFSM) SPIN和线性时态语言 UML 语义转换 标准RBAC模型 扩展的RBAC模型和基于粒计算的RBAC模型 符号模型检验 BAN逻辑模型
基于模型的软件测试 基于模型的测试(Model-based testing,MBT)是利用模型来生成相应的测试用例,然后根据实际结果和原先预想的结果的差异来测试系统 先从概念上形成模型,然后试图用数学的方法来描述这个模型,形成仿真模型,完成所需的测试
MBT的优缺点 缺点 优点 测试效率高 对逻辑复杂故障测试效果好 容易实现自动化测试 误报问题 漏报问题 模型机理
软件测试模型 故障模型。 安全漏洞模型 差性能模型 并发故障模型 不良习惯模型 代码国际化模型 易诱骗代码模型
检测步骤 预处理/预编译 词法分析(Lexical Analysis) 语法分析( Parsing) 和语义处理( Semantic Analysis) 抽象语法树生成 控制流图生成 IP 扫描 人工确认。
扩展有限状态机方法 有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件
EFSM示例-1
EFSM示例-2
EFSM工具 - FSME
基于风险的测试 测试就是“对软件系统中潜在的各种风险进行评估的活动 基于风险的测试是指评估测试的优先级,先做高优先级的测试,如果时间或精力不够,低优先级的测试可以暂时先不做 软件产品的风险度可以通过出错的影响程度和出现的概率来计算
风险度量
风险测试步骤 列出软件的所有功能和特性; 确定每个功能出错的可能性; 如果某个功能出错或欠缺某个特征,需要评估对用户使用软件产品的影响程度; 根据上面两个步骤,计算风险度; 根据可能出错的迹象,来修改风险度; 决定测试的范围,编写测试方案
模糊测试方法 模糊测试(Fuzz testing)方法,简单的说,就是构造大量的随机数据作为系统的输入,从而检验系统在各种数据情况下是否会出现问题 模糊测试方法可模拟黑客对系统发动攻击测试,完成安全性测试,并能应用于服务器的容错性测试 参考网站: http://www.cs.wisc.edu/~bart/fuzz/fuzz.html http://sourceforge.net/projects/taof/ www.genexx.org/dfuz www.fuzzing.org 书中两个示例
ALAC测试和随机测试方法 ALAC,是Act-like-a-customer(象客户那样做)的简写,ALAC测试方法是一种基于客户使用产品的知识开发出来的测试方法,它的出发点是著名的Pareto 80/20规律
Q & A