坚持上王者的青铜人

如何套路我们的产品 ---软件测试艺术学习记录

在我们测试过程中,经常听到这样的话:那个测试很容易的,点几下就是了,几分钟就搞定了。事实真的是这样吗? 在我们正式介绍软件测试的概念前。我们来看一个小小的测试案例!

要求我们设计一组测试用例,适当的测试我们的程序是否正确。下面是对这个程序的描述

从一个输入框中读取三个数值,这三个数字代表了三角形三条边的长度,程序需要显示提示信息,指出该三角形究竟是不规则三角形、等腰三角形还是等边三角形

实现代码如下:
    
    def IsTriangle(a,b,c):

        if a+b>c and a-b<c:

	    TriangleType(a,b,c)
        else 
	    print("Not truabgle")

    def TriangleType(a,b,c):

        if a != b and b != c and a!=c:
	    print("It's sealene triangle")
        elif a == b and a==c:
	    print("It's equilateral triangle")
        else:
	    print("It's isosceles triangle")
	
	
    if __name__ =="__main__":

        a = input()
        b = input()
        c = input()

        IsTriangle(a,b,c)

单纯从题目中我们可以提取到那些信息?

1. 输入框提取三个数值组成三角形
2. 三角形分为不规则三角形、等腰三角形、等边三角形
还有其他有用的信息吗?

根据这些有用的信息,我们需要设计那些测试用例来验证我们程序的正确性呢?

1. 是否有这样的测试用例:三个值代表了一个不规则的三角形
2. 是否有这样的测试用例:三个值代表了一个等腰三角形
3. 是否有这样的测试用例:三个值代表了一个等边三角形
4. 是否有这样的测试用例:某边的长度等于0
5. 是否有这样的测试用例:某边的长度为负数
6. 是否有这样的测试用例:某两边长度的和等于第三边的长度 注意三角形成立的条件是任意两边之和大于第三边
7. 根据第六点是否有这样的测试用例:某两边的长度之和小于第三边
8. 是否有这样的测试用例:三边的长度都为0
9. 是否有这样的测试用例:三边的长度都为非整数
10. 是否有这样的测试用例:输入边长的个数不对,比如少了一条边的长度或者多了一条边的长度
11. 是否有这样的测试用例:针对上面所列的所有输入值有对应的输入验证

还有其他情况需要考虑吗?

程序出现问题会有千奇百怪的原因,即使我们的程序满足了上面的所有测试用例,也不能确保我们的程序不会出现问题。但根据上面设计的测试用例,我们已经对程序进行了一定程序的测试,暴露了绝大部分问题。这个程序是可以交付使用的

每个人的想法都是有限的,所以我们不可能会考虑得非常全面。很多时候我们能考虑到80%的情况就已经非常优秀了。 是不是特别神奇。这里出现了80%。

我想用这个简单的程序来说明,测试并不是一件容易的事情。一个十几行的测序都需要这么多的测试用例来验证。在想想我们云开发平台的66万行代码,visual studio几个G的代码。 windows 几十个G的代码。由此可以说明测试是一个繁杂,漫长的过程!

软件测试基础

什么是软件测试? 软件测试的定义是什么?

一部分人认为软件测试是为了证明软件不存在问题或者是为了证明软件能正确实现其功能的过程。 当然这种说法有他一定的道理。 但我认为软件测试应该定义为发现软件错误的过程。测试是不能避免程序出错的。 我们只是通过不断的测试减少程序出错的几率。

在测试过程中,执行一次有效的测试用例没有发现bug是可喜的。 但个人认为对于测试工程师来说测试过程中发现bug的意义远远大于测试用例通过的意义。

软件测试经济学

这是一个很意思的话题。软件测试居然涉及经济学。是不是很意外。要不要来点惊喜?

软件测试经济学主要体现在测试成本。对于软件来说测试越细越好,但是对于公司来说,测试是需要人、工具、时间来完成的每一项都代表着经济支出。为了应对测试经济学。我们为了在有限的成本里面完成更好的测试则需要在开始测试之前建立测试策略。黑盒,白盒就是最常见的两种测试策略

黑盒测试

黑盒测试是一种非常重要的测试策略,我们有可以称之为数据驱动测试或者输入\输出驱动的测试。使用这种测试策略时,我们将程序视为不可见内部结构的盒子。我们不关心程序内部是如何实现的。重点关注在程序是否按照预定的逻辑得到结果。 其实很多人都或多或少知道黑盒测试,只是没有仔细去了解过黑盒测试的具体策略。

黑盒测试判定的标准为“穷举输入测试”,意思就是将所有可能的输入条件都做为测试用例,这样做的原因是因为程序对我们来说就是一个黑盒子,因此能确定条件成立的唯一标准就是试验所有的输入情况。 之前我们测试三角形的例子就是穷举输入条件。典型的黑盒测试

我们可以思考一个问题,穷举输入测试在我们日常的测试过程中真的能实现吗?或许一个小的程序是可以实现的,但过于庞大的程序呢? 如windows?

我们很容易就能得出结论,穷举是不可能实现的。 一是我们无法保证一个程序是无错的、 二是需要考虑测试成本。我们只能在有限的输入条件下尽可能多的发现问题。这就需要我们考虑测试用例的设计策略, 这个点我们将在后面的介绍中指出。

白盒测试

白盒测试又可以称为逻辑驱动测试,允许我们去检查程序的内部结构,这种测试逻辑是对程序内部逻辑结构进行检查,从而得到测试数据。
白盒测试在测试过程中我们要保证每条语句都能得到执行。这种方式我们称之为穷举路径测试。只有测试用例执行了所有的控制流路径,那么程序才有可能得到完全测试。 穷举路径测试就和穷举输入测试一样,非但不可能也是不切实际的。

白盒测试也是有缺陷的。白盒测试过多的强调了逻辑控制流路径。但是如果程序开始的设计都是错的,那么即使完成了测试也是没有意义的, 比如我们程序希望的是冒泡排序,结果程序员在开始的时候就写成了插入排序,那么我们即使验证了这个插入排序是正确的。 结果其实也是没意义的。

软件测试原则

在软件测试过程中,我们应该遵循以下9个原则

1. 测试用例中一个必须部分是对输出或者结果进行定义。 这里的意思是 我们必须知道 这个测试用例 想要得到的结果,这样我们才能知道程序是否正常。
2. 程序员应该避免测试自己写的程序
3. 编写软件测组织部应该测试自己编写的程序
4. 应该仔细检查每个测试的执行结果
5. 测试用例不仅应该根据有效的输入和预料到的输入情况。也应该适量考虑无效的输入和未预料到的输入情况
6. 检查程序是否做了其应该做的只是测试的一半,另外一半是检查程序是否做了其不应该做的
7. 应该避免测试用例用了一次就弃用,除非程序是一次性的
8. 计划测试工作时,不应假定其不会发生错误
9. 程序某部分应该存在更多的错误的可能性,与已经发现的错误数量应该成正比

原则一:测试用例中一个必须部分是对输出或者结果进行定义

这条显而易见的原则在软件测试过程中是最常见的错误之一。他是基于工作人员的工作心理,由于某个测试用例的预期结果事先没有得到定义, 由于“所见即所想”的现象存在,某个实施而非,实际上是错误的的结果可能会被解释成正确的结论。因此,一个测试用例必须包括两个部分:
    1. 对程序输入数据的描述
    2. 对程序在上述输入数据下的正确输出结果的精确描述
我们必须形成特定的认识:没有经过验证的功能都可能是错的。测试人员必须有怀疑精神

原则二:程序员应当避免测试自己编写的程序。

这是因为程序员自己眼中的代码都是完美的。当程序员“建设性”的完成了设计和编写程序之后,很难让他去怀疑自己的代码,很难让他突然改变视角以一种“破坏性”“怀疑性”的眼光来审查程序。 另外大多数人都有逃避心理会下意识的避免找出错误,担心受到同事,上司,客户或主管的惩罚。

再有一点,程序员错误的理解了产品需求,如果是这种情况,程序员可能会带着同样的误解来测试自己的程序

原则三:编写软件的组织不应当测试自己编写的软件

这个理论和原则二比较相识。而且在大多数情况下,主要是根据给定时间、特定成本范围内开发软件的能力来衡量编程组织或者项目经理。 度量时间和成本目标是比较容易的,而定量的衡量软件的可靠性是极其困难的。即便是合理规划和实施的测试过程,也可能被认为降低了完成进度和成本目标的可能性。甚至我们可以恶意猜测,为了赶进度和节约成本,有人会恶意的隐瞒程序所暴露的问题。 因此,编程组织难以客观的测试自己的软件

当然我们不是说编程组织不能发现程序中的问题。而更经济的方式应该由客观、独立的第三方来进行测试。

原则四:应该彻底检查每个测试的执行结果

这个原则是最显而易见的原则,但有时候也可能被忽略的。 我在以前的测试过程中也发现,很多错误的结果已经可以很清楚的在输出中看到,但还是没有找出那些错误。在后续测试中发现的问题,很多都是之前测试中所遗漏的

原则五:测试用例不仅应该根据有效的输入和预料到的输入情况。也应该适量考虑无效的输入和未预料到的输入情况

在软件测试过程中,我们有一个很自然的倾向,即将重点集中在有效和预期的输入情况上,而忽略了无效的和未预料到的情况。

原则六:检查程序是否做了其应该做的只是测试的一半,另外一半是检查程序是否做了其不应该做的

这条原则是原则五的必然结果,我们必须检查程序是否做了我们不希望的负作用。比如我们手里的项目 智慧公寓。如果他在生成租客账单时也给其他租客生成了账单,那么这个程序一定是不正确的程序

原则七:应该避免测试用例用了一次就弃用,除非程序是一次性的

目前很多软件的寿命是比较短的,很多时候在极短的时间内可能就会发生很大的变化。 而针对每次测试。我们都需要设计大量的测试用例来验证我们的程序。 如果测试用例不可复用。 在成本上也会造成极大的浪费。

同时,即使是同一款软件,我们对程序的重复测试极少会同上一次一样严格。这就意味着,如果对程序的更改导致了程序某个先前可以执行的部分发生了故障,如果不复用之前的测试用例这个故障是不会被发现的,所以保留测试用例,当程序其他不见发生更改后重新执行,这就是我们所谓的“回归测试”。

原则八:计划测试工作时,不应假定其不会发生错误。

测试是一个证明程序正确运行的过程,是为了发现错误而执行程序的过程。如果开始就假定其不会发生错误,这种行为与测试工作是背道而驰的

原则九:程序某部分应该存在更多的错误的可能性,与已经发现的错误数量应该成正比

这个原则是很多前辈通过经验,教训总结出来的。该原则的另外一种说法是,错误总是倾向于聚集存在。尽管没人能够解释这种现象。但我们需要对程序容易存在错误的部分进行额外的测试。

测试分类

从测试方法的角度可以分为手工测试和自动化测试。
手工测试:不使用任何测试工具,根据事先设计好的测试用例来运行系统,测试各功能模块。
自动化测试:利用测试工具,通过编写测试脚本和输入测试数据,自动运行测试程序。目前最常用的自动化测试工具是基于GUI的自动化测试工具,基本原理都是录制、回放技术。好的测试员都是喜欢自己写脚本而不是单纯的录制回放。录制回放很多时候不能满足自身特定的需求,所以自己编写脚本才是最好的方式

 

从整体的角度可以分为单元测试、集成测试、系统测试、确认测试。

单元测试:是针对软件设计的最小单位—程序模块,进行正确性检验的测试工作。一般包括逻辑检查、结构检查、接口检查、出错处理、代码注释、输入校验、边界值检查。

单元测试的依据是系统的详细设计;一般由项目组开发人员自己完成。测试人员可以尝试去多读读源代码,这样在测试的时候也可以考虑设计编码的缺陷 优化

集成测试:在单元测试的基础上,将所有模块按照设计要求组装进行测试。一般包括逻辑关系检查、数据关系检查、业务关系检查、模块间接口检查、外部接口检查。开发人员需要进行联调。测试人员在冒烟测试时需要联测

系统测试:系统测试是在所有单元、集成测试后,对系统的功能及性能的总体测试。

确认测试:模拟用户运行的业务环境,运用黑盒测试方法,验证软件系统是否满足用户需求或软件需求说明书中指明的软件特性(功能、非功能)上的。

从测试原理上分为:白盒测试、黑盒测试和灰盒测试。

白盒测试:是通过程序的源代码进行测试而不使用用户界面。这种类型的测试需要从代码句法发现内部代码在算法,溢出,路径,条件等等中的缺点或者错误,进而加以修正。

黑盒测试:是通过使用整个软件或某种软件功能来严格地测试, 而并没有通过检查程序的源代码或者很清楚地了解该软件的源代码程序具体是怎样设计的。测试人员通过输入他们的数据然后看输出的结果从而了解软件怎样工作。在测试时,把程序看作一个不能打开的黑盆子,在完全不考虑程序内部结构和内部特性的情况下,测试者在程序接口进行测试,它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收和正确的输出。

灰盒测试:灰盒测试,是介于白盒测试与黑盒测试之间的,可以这样理解,灰盒测试关注输出。对于输入的正确性,同时也关注内部表现,但这种关注不象白盒那样详细、完整,只是通过一些表征性的现象、事件、标志来判断内部的运行状态,有时候输出是正确的,但内部其实已经错误了,这种情况非常多,如果每次都通过白盒测试来操作,效率会很低,因此需要采取这样的一种灰盒的方法。

软件测试用例设计

软件测试工作中最重要的因素就是设计和生成有效的测试用例,我们可以这样认为,测试用例是无尽的,但时间和成本是有限的。那么在所有可能的测试用例中,那个子集最有可能发现最多的错误?

软件测试用例设计方法为我们提供了答案。

接下来我们主要讨论黑盒和白盒测试的方法:

黑盒测试: 等价类划分、边界值分析、因果图分析、错误猜测
白盒测试:语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、多重条件覆盖

首先我们来看一段简单的代码:

def foo(a,b,x):
    if a>1 and b == 0:
    
        x = x/a
        
    if a == 2 or x<=1:
    
        x = x+1

针对这个程序我们可以看一下简单的逻辑图:

image

我们为了充分测试上面的程序,最好的办法就是覆盖所有的条件,而在软件测试用例设计方法中判定覆盖或分支覆盖是较强的逻辑覆盖准则

上面这个程序涉及到四个条件 a>1 b=0 a=2 x>1. 因此我们需要足够的测试用例使得在第一个if语句出现a>1 a<=1 b=0 b<>0,在第二个if语句处出现 a=2 a<>2 x>1 x<=1

根据判定覆盖和我们的逻辑图,我们能够设计的测试用例为:
1. ace 2. abc 3. abd 4. acd

仔细想想我们是否覆盖了所有的逻辑?

通过上面测试用例的设计 我们是不是只关注了if条件的判定,而没有关注if 条件的验证?

我们细分一下这个程序的逻辑图
image

这样细分后是不是更清晰?
如果通过上面这个逻辑来执行,我们是不是保证了把每个条件的所有可能的结果都执行了一次。 这里我们就不一一列举每个测试用例了。

通过上面的例子 我们简单的介绍了一个功能测试 设计测试用例的方法,接下来我们将详细介绍每一种方法

等价划分

使用等价划分方法设计测试用例主要有两个步骤: 1. 确定等价类 2. 生成测试用例

1. 确定等价类

确定等价类是选取没一个输入条件并将其划分为两组或者更多组,我们可以使用下面的表格来进行划分。注意我们确定了两类等价类:有效等价类代表对程序的有效输入,而无效等价类代表的则是其他任何可能的输入条件(即不正确的输入值)

外部条件 有效等价类 无效等价类

在给定了输入或者外部条件后,确定等价类是一个启发式过程,下面我们给出一些例子:

1. 如果输入条件规定了一个取值范围(如输入1-999的数值), 那么我们可以确定一个有效等价类(1<x<999),以及两个无效等价类(x<1、x>999)

2. 如果输入条件规定了取值的个数(如汽车可以登记一至六名车主),那么就应该确定出一个有效等价类(三个车主)和两个无效等价类(没有车主、大于六个车主)

3. 如果输入条件规定了一个输入值的集合,而已有理由认为程序会对每个值进行不同处理(如我们的工资只能输入正数值),那么我们可以等到一个有效等价类(徐总的工资是35642.5)和两个无效的等价类(小王的工资-1.2,或者输入abc)
2. 生成测试用例
1. 为每个等价类设置一个不同的编号
2. 编写新的测试用例,尽可能多的覆盖那些尚未被覆盖的有效等价类,直到所有的有效等价类都被测试用例所覆盖
3. 编写新的测试用例,尽可能多的覆盖那些尚未被覆盖的无效等价类,直到所有的无效等价类都被测试用例所覆盖

一样的举个例子,智慧公寓注册

外部条件 有效等价类 无效等价类
电话号码 正确的电话号码 座机号码,非电话号码字符串
验证码 有效正确验证码 错误验证码、过期验证码、其他手机号的验证码
密码 符合条件的密码 密码过短, 密码过长、密码没有满足条件、密码有非法字符

帐号注册时需要同时满足上面的三个条件, 所以我们生成测试用例时,我们可以自由组合上面的等价类,形成我们的测试用例

边界值分析

前辈的经验证明,考虑了边界条件的测试用例与其他没有考虑边界条件的测试用例相比,具有更高的回报率。所谓的边界条件,是指输入和输出等价类中有那些恰好处于边界或者超过边界或者在边界以下的状态。

同样,我们给出一些例子:

1. 如果输入条件规定了一个输入值范围,那么应针对范围的边界设计测试用例。举例来说:考试系统录入学生分数时限制分数是从0-100 那么针对-0.1 0 100 100.1的情况设计测试用例

2. 如果输入条件规定了输入值的数量,那么应该针对最小输入值、最大输入值、以及比最好数量少一个、比最大数量多一个来设计测试用例。举例来说:智慧公寓添加房源时,小区名称为必填,且字数不能超过20个字, 那么我们应该针对不输入小区名,小区名为一个字,小区名为20个字,小区名为21个字来设计测试用例

3. 软件程序中有些值是比较特殊的,比如设计一个变量是int,那么他能接受的范围是-32768~32767, 但在不同平台上,int值的范围是不一样的,所以要充分考虑边界值

因果图

佛说有因必有果。因果图就是让我们知其然也知其所以然,它有助于我们选择出高效的测试用例集。同时可以支出规格说明的不完整性和不明确的地方。前面的边界值,等价划分都是基于功能的,因果图能帮助我们去思考程序,判定程序的合理性,产品设计的正确性。举个比较形象的例子: 因为老总有很多钱,所以他买了一栋别墅。 这个因果关系是正确的! 同样一个错误的例子: 因为老总很有钱,所以他想请大家吃饭。 这个逻辑是说不通的对吧! 要是能说通就比较完美了

使用因果图编写测试用例时我们可以按照下面的步骤来:

1. 将产品说明分解为可执行的片段。这是一个必须的步骤。因为因果图不善于处理较大的功能说明。我们需要细化到每个功能点,而不是在系统层面上做因果分析
2. 确定每个功能点的因果关系。 所谓的“因”是指一个明确的输入条件或者输入条件的等价类,所谓的“果” 是指通过程序对输入条件的处理计算后得到的输出。比如智慧公寓我们删除了系统超级管理员的角色,那么所有超级管理员登录系统就只能查看到首页。 这就是因果关系
3. 分析功能点的说明,并将去转换为链接因果关系的布尔图,得到所谓的因果图
4. 通过因果图联系,将每个联系都转化为一个测试用例

因果图到测试用例转化过程中还涉及到判定表,但判定表太抽象,有时反而不利于理解,所以我常常省略了判定表的建立

错误猜测 这特么就是传说中的玄学

错误猜测法不是一种常用的测试方法。因为错误猜测发是一项依赖于直觉的非正规的过程。我们很难去规范错误猜测法怎么做,它是测试员通过自己的经验去判断程序可能会出错的地方。他的基本思想就是列举出可能犯的错或者容易发生错误的地方的清单。在根据错误清单,编写测试用例

测试策略

上面介绍了四种设计测试用例的方法,但每种方法都不能单独提供一个完整的测试用例集。我们需要将多个测试用例设计方法组合成测试策略。一组合理的策略如下:

1. 如果产品说明中包含了输入条件组合的情况,首先使用因果图分析
2. 任何情况下都要使用边界值分析法,很多程序问题都发生在边界上
3. 应为输入和输出确定有效和无效等价类
4. 使用错误猜测玄学增加更多的测试用例

最后强调一次 再多的套路都不能保证发现所有的错误。它是一个艰巨漫长的工作任务。谁在你耳边说测试很简单,点几下就完了! 我们是可以宣告他的死刑的

posted on 2017-10-11 08:48  坚持上王者的青铜人  阅读(225)  评论(0)    收藏  举报

导航