分享

单元测试、集成测试之入门一【读书札记】

 KILLKISS 2014-03-03


【格言1】若程序的某项功能没有经过自动测试,那该功能基本等于不存在

【格言2】敏捷测试的第一步就是不要敏捷,先一步步把自动化做好,把持续集成做起来,创建更多的测试工具来提高测试效率,把质量反馈系统做起来,代码质量检查做起来等等,这些建立起来后,你就很敏捷了

【格言3】如果在功能测试和单元测试选一个来做的话,那你应该选择功能测试,70%的应用程序错误都可以通过功能测试来找回的。

 

 

参考文献

1、《junit in action》 电子工业出版社

2、《软件测试的艺术》 机械工业出版社【力荐】

一、理论知识点

1、什么叫做测试阶段,什么叫做测试方法?

    测试阶段:单元测试、集成测试、功能测试、系统测试,这个叫做测试阶段

    测试方法:从大的分类讲,白盒测试、黑盒测试、灰盒测试、人工测试,这个叫做测试方法

2、什么样的测试叫做单元测试?单元到底是什么,多小多大的东西才叫单元?

    百度百科说法:要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。

    junit in action》说法:单元测试测的是独立的一个工作单元。在java应用程序中,“独立的一个工作单元”常常指的是一个方法。一个工作单元是一项任务,它不依赖于其他任务的完成。

    《软件测试的艺术》说法:单元测试是对程序中的单个子程序、子程序或过程进行测试的过程,是对构成程序的较小模块的测试。

    各位看官,你们看懂了么?反正我没看懂。上面有说单元是java中的一个类,又说是java代码中的一个函数方法,这到底是闹哪样。以下是我猜测的结论:被测单元的粒度大小是由程序员规定的,“单元”不仅仅是对应一个代码中方法或某个类,应该说一个具有独立任务的功能代码短(软件编程有一个单一原则,一个方法对应一个功能点)。

3、什么样的测试叫做集成测试?

     单元A  +  单元B = 更大单元C

     单元A:保存用户数据信息到数据库

     单元B:下发邮件(短信)到用户邮箱(手机)

     更大单元C:注册功能

插曲,请回过头来理解这句话:“《junit in action》一个工作单元是一项任务,它不依赖于其他任务的完成”。第一:单元A和单元B互不依赖;第二:输入可以使用其他单元的输出数据

集成测试:各个单元组合测试,完成某一大功能的测试。单元A测试正确之后,把单元B加入测试。注意:尽量使用增量测试,就是说A,B,AB,C,ABC,D,ABCD这样顺序,而不是A,B,C,D,ABCD这样,虽然少了一点,但是错误不好定位。

4、什么样的测试叫做功能测试?

    看看上面的注册功能测试C,可以当作功能测试中的一个步骤了。

    功能测试:测试软件表现的是不是符合spec上面规定的

5、什么样的测试叫做系统测试?

    系统测试:测试软件表现的是不是符合最初目标(用户需求),包含安全性测试、性能测试、容量测试、可靠性测试等。

6、黑盒测试有哪些方法?

    黑盒测试:有正确输入和预期的输出,用正确的输入来测试软件,看看输出数据(表现状态)是否和预期的一致。

我们只讲主要的:等价类测试、边界分析值测试、错误猜测、因果图等方法

等价类测试:

    有效(正确的)输入作为一类,其他无效的作为另一类,注意了其他无效的应该是分开的

    比如用户名8到16个字符,正确的有效的一个类:对于8个小于16个

    错误的类需要分开,而不能写成这样作为一个等价类:小于8或者大于16,因为编译器遇到小于8的时候,就不会忽略后面的错误了。

    错误等价类:1、小于8    2、大于16   独立性

 

边界测试:一般是对数量、个数啊、是、否、有、无、相同、不同、最高、最低进行边界数据测试(包含输出的结果反推到输入数据,比如y=sinx函数,他对输入x没有边界,但是对输出y是有边界的,那就是当y=1的时候,x的那个值需要被测试)

    原则:

    1、有效值边界,最大值、最小值、比最小少一点、比最大大一点,比如用户名是8到16位,那么边界测试用例数据包含:8,16,7,17

    2、输入和输出是有序的,注意第一个和最后一个元素

    3、数据库最大记录数,最小记录数

 

因果图测试:一般是两个输入组合作为条件产生输出的。比如买雪碧,需要两个输入条件1:投入钱币数额2、用户按了哪个按钮。

可能用户投入的是1.5元,按了可乐,那自动饮料机就会输出可乐了。。。输出由两个输入组合决定。

错误猜测:靠猜,可能某些容易犯错误。

 

7、白盒测试有哪些方法?

    白盒测试:语句覆盖测试、判定覆盖测试、条件覆盖测试、判定/条件覆盖测试、多重条件覆盖测试等。

    亲,在实际项目中比较靠谱的是多重条件覆盖测试,其他的你可以忘掉。

不想看具体说明的,下面可以跳过

-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-

白盒测试:覆盖程序逻辑结构(源代码)的程度,完全的白盒测试是将程序中每条路径都执行到,然而对有带有循环的程序,完成路径测试是不切合实际的。

 

完全路径测试是测试不出来真正的错误。语句覆盖——每条语句都覆盖过——较弱的逻辑覆盖准则

 

判定覆盖或分支覆盖——较强的逻辑覆盖准则。要求必须编写足够的测试用例,使得每一个判断都至少有一个为真和为假的输出结果。每一条语句都必须覆盖过

If(b==2 || a > 1) 这种判断覆盖,他只负责if真和假的路径,测试用例可以这样 b=2 a=1或者b=3 a=1 。但是正确的语句应该是if(b==2 || a <1),这样测试用例测试不出来

 

条件覆盖——要对if中的判断语句每个条件的所有可能都覆盖到,比如IF(A&B) 测试用例可以是:1(A为真,B为假)  或者 2(A为假,B为真),你瞧瞧A的真假都设计到,B的真假都设计到了,他们满足条件覆盖的测试用例,但是if语句中的then没有被执行。于是引入了判定/条件覆盖

 

判定/条件覆盖准则——较强的测试用例设计准则,每个判断中的每个条件的所有可能的结果至少执行一次,每个判断的所有可能的结果至少执行一次。要求是设计出充足的测试用例。

还是例子IF(A&B) ,1:条件A和B所有可能结果是A为真,A为假,B为真,B为假,

2:判断的所有可能A&B为真 , A&B为假。

测试用例:1(A为真,B为真) 2(A为假,B为假)满足这个准则,想想有问题么?

具体点if(a>0 && b>1) 其中A代表a>0, B代表b>1

测试用例:1(a=2, b=3)真,执行then语句;2(a=-3,b=1)假,不执行then语句。满足上面判定/条件覆盖准则

问题:其实逻辑判断语句b>1是错误的,他应该是b>2 正确的逻辑代码是if(a>0 && b>2)

结论:判断/条件覆盖准则的测试用例发现不了这个问题。

 

多重条件覆盖准则——,将每个判定中的所有可能的条件结果进行组合,所有的入口点都至少执行一次。注意了,不是多重判定/条件覆盖准则,但是他满足了判定准则、条件覆盖准则(不用说)、判定/条件覆盖准则。IF(A&B) 对于这个,测试用例必须覆盖下面4个组合是:

A

B

结果

0

0

0

0

1

0

1

0

0

1

1

1

\

较上面多了2个

他做到了条件组合,但是并没有做到一个方法内部的判定组合

If(a>1 && b==0) {

    X = x /a;   。。。。。。。。。。。。。。。。。。。。。。。。。运行1

}

If(a == 2 || x > 1) {

    X = x + 1;。。。。。。。。。。。。。。。。。。。。。。。。。。。运行2

}

8种条件组合

 

a

b

x

结果

1

>1

=0

 

1

2

>1

<>0

 

×

3

<=1

=0

 

×

4

<=1

<>0

 

×

5

=2

 

>1

2

6

=2

 

<=1

×

7

<>2

 

>1

×

8

<>2

 

<=1

×

 

只要测试用例覆盖了以上的组合就可以了

a

b

x

覆盖了

2

0

4

1,5

2

1

1

2,6

1

0

2

3,7

1

1

1

4,8

 

上面给出的4个具体的测试用例,覆盖了所有的条件组合。同样也覆盖了所有的语句,但是某些组合判定没有被覆盖到。比如1,6组合,运行了语句1,其他不运行。

-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-

总结:从上面几个来看,最完整的应该建议使用多重条件覆盖测试准则。

8、哪个测试阶段会使用某些测试方法?

    1、单元测试

           若你是敏捷开发者,单元测试肯定是黑盒测试方法。(因为先写测试代码)

           若不是,单元测试使用白盒测试、黑盒测试多种方法组合

    2、集成测试

    集成测试是单元测试基础上,如何把单元模块组装起来一起测试,测试方法采用黑盒。组装方式有自顶向下和自下向上两种。

比如之前说的

单元A  +  单元B  +  单元C = 更大单元D

 

    单元A:保存用户数据信息到远程UCenter数据库

     单元B:下发邮件(短信)到用户邮箱(手机)

     单元C:保存用户数据信息到本地数据库

     更大单元D:注册功能,保存成功之后

     对于单元D,要求是单元A成功后,才能运行单元B和C,他们的关系A在顶端,BC在下面

     自顶向下:A, B, C,AB,ABC

     自下向上:B ,C, A,BC,ABC

     原则:关键单元先测,然后在组合其他的

注意:A是调用远程接口,我们在开发的时候远程代码还没有完成,那我们该如何完成上面的集成测试了?使用mock,自己模拟写一个A的功能,此功能不处理任何逻辑。或者用stub,需要你实现逻辑。Ps:为什么计算机中把stub翻译成桩,很难理解!!

9、一般测试的步骤是什么样的?

    第一步:单元测试

           1、选择测试方法,到底是选择白盒测试、还是黑盒测试

           一:一般是使用组合测试方法,但是如果是敏捷开发方式,只能用黑盒测试方法了

           二:选择白盒测试中的多重条件测试准则

           三:如果接口文档中说输出是由输入条件组合才能得出,请使用黑盒的因果图分析方法

           四:任何情况都要使用边界值分析方法

           五:其他情况,使用等价类和错误猜测技术增加更多的测试用例进行补充

           2、设计测试用例

           根据1中列出来的测试方法来列出测试用例,覆盖白盒测试中的条件组合,

           表格如下:

测试用例编号

输入

预期的输出

注释

1

 

没有返回值,表示成功

 

2

。。。。

。。。。

。。。

           原则1:测试用例尽量少

           原则2:一个测试用例覆盖尽量覆盖多个组合(其实和原则1相辅相成)

3、起单元测试方法名称

           某被测方法或者功能userRegister

           原则1:测试名使用驼峰式 xxYyyZzz

           原则2:命名规则,由以下组成test+被测方法+预期输出的类型。如上面测试用例1,他的命名testUserRegisterSuccess。

           原则3:一个测试方法,只处理一种预期类型(个人理解)。

在《junit in action》书中说道:“一个单元测试等于一个测试方法,不要试图把多个测试塞进一个方法” 什么意思啊?看懂了么?

表格如下:

方法名

检查到的测试用例(这里是测试用例表中的序号)

testAccountExistUserInfoNull

9

testAccountExistUsernameExisted

3

testAccountExistEmailExisted

4

testAccountExistMobileNumExisted

6

testAccountExistUserTrue

1、2、5

4、写测试代码方法内部使用Assert.fail(“未测试”);Why?避免遗漏测试方法,养成好习惯:)

       5、最后填充测试方法

    原则一:对于assertTrue/assertNotNull/assertNull/assertFalse测试方法,请记住第一个参数sring需要写,便于失败原因。

    原则二:把共有初始化行为放在setUp方法中

    第二步:集成测试

           1、有时候需要写mock或者stub

           2、重要功能的单元测试先加进来,然后加上不重要的单元

    其他:功能测试、系统测试,这里不讲

二、具体实践

0、已有模块代码:      

此模块的测试描述:用户判断用户名、手机号码、邮箱地址是否重复

1、选择测试方法,到底是选择白盒测试、还是黑盒测试

    N1:选择白盒测试中的多重条件测试

    N2:选择黑盒测试中的边界值分析法

    N3:其他补充(错误猜测法)

2、设计测试用例

    N1:基于上面多重条件测试方法。

把所有的if和while等判断语句找出来

判定(代码行数)

条件

正确结果

错误结果

297

User.getUsername()!=null

用户名不为空

用户名为空

299

USERNAME_EXIST.equals(resultStr)

用户名已经存在

用户名不存在

304

User.getEmail()!=null

邮箱地址不为空

邮箱地址为空

306

EMAIL_EXIST.equals(resultStr)

邮箱地址已经存在

邮箱地址不存在

311

User.getMobile()!=null

手机不为空

手机为空

311

User.getMobile().getMobileNum()!=null

手机号码不为空

手机号码为空

313

MOBILE_EXIST.equals(resultStr)

手机号码已经存在

手机号码不存在

    1、用户名为空

2、用户名不为空,用户名重复

3、用户名不为空,用户名不重复

4、邮箱地址为空

5、邮箱地址不为空,邮箱地址重复

6、邮箱地址不为空,邮箱地址不重复

7、手机号码为空

8、手机号码不为空,手机号码重复

9、手机号码不为空,手机号码不重复

 

那么测试用例必须覆盖上面的9个组合,注意了一个测试用例尽量覆盖多个上面的组合条件

 

测试用例编号

输入

预期的输出

注释

1

username=””

email=”email”//邮箱地址可以注册

mobileNum=””

程序状态不变

Result=0

 

覆盖了条件:1、6、7

2

username=“username”

email=””

mobileNum=””

此用户名不存在数据库中

需要给出数据库预置的内容

程序状态不变

Result=0

 

覆盖了条件:

3、4、7

3

username=”existed_username”

email=””

mobileNum=””

程序返回1

覆盖了条件:

2、4、7

4

email =”existed_ email”

username=””

mobileNum=””

程序返回2

覆盖了条件:

1、5、7

5

mobileNum =“123123123123”

此手机号码不存在数据库中

需要给出数据库预置的内容

Username=””

Email=””

程序状态不变

Result=0

 

覆盖了:1、4、9

6

mobileNum =”32132132131”

此手机号码重复

Username=””

Email=””

程序返回3

覆盖了:1、4、8

7

Username=””

Email=””

mobileNum=””

程序返回4

覆盖了:1、4、7

N2:选择黑盒测试中的边界值分析法

        还记的上面讲的原则么?

原则:

    1、有效值边界,最大值、最小值、比最小少一点、比最大大一点,比如用户名是8到16位,那么边界测试用例数据包含:8,16,7,17

    2、输入和输出是有序的,注意第一个和最后一个元素

    3、数据库最大记录数,最小记录数

但是我发现这个单元很难找到边界值,单元并没有对输入值范围、大小进行明确的规定。读者,您有好的用例么?帮忙给出。谢谢了。

N3:其他补充(错误猜测法)

 

User如果为空,上面的方法就是废物,而且运行期会抛出空指针异常。所以有时候设计用例的时候,会反过来发现代码设计的有问题。这部分白盒测试没办法指导。

测试用例编号

输入

预期的输出

注释

8

User=null

程序返回4

返回是无效参数

其他的错误猜想,没想到,请各位看官指点一二。

3、起单元测试方法名称

 

方法

检查到的测试用例

testAccountExistUserInfoNull

7,8

testAccountExistUsernameExisted

3

testAccountExistEmailExisted

4

testAccountExistMobileNumExisted

6

testAccountExistUserTrue

1、2、5

 

4、写测试代码方法内部使用Assert.fail(“未测试”);Why?避免遗漏测试方法,养成好习惯:)

   

5、最后填充测试方法

待续~

 

三、总结

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多