摸着石头过河——从黑盒到灰盒

来源:互联网 发布:只有我知bilibili双语 编辑:程序博客网 时间:2024/06/05 05:06

我想,作为一个软件测试人员,如果常年一直都处于黑盒和手工测试的话,必然成长速度是不会很快的;故,从黑盒向灰盒转变,也是一个对自己的职业生涯的提升,后面几篇日志我决定分享一下自己的转变过程,由于没什么人来指导自己,属于摸着石头过河过程中的中自我总结。

  先简述一下灰盒测试的基本概念,Google一下“灰盒测试:是介于白盒测试黑盒测试之间的,可以这样理解,灰盒测试关注输出对于输入的正确性,同时也关注内部表现,但这种关注不象白盒那样详细、完整,只是通过一些表征性的现象、事件、标志来判断内部的运行状态,的一种测试方法”

  为神马要用灰盒测试,主要还是因为互联网产品小步快跑,快速迭代的特性。由于强调了个“快”字,从产品需求,到开发提测,再到测试完成发布,间隔的时间会非常之短。如果只借助传统的黑盒测试,质量上不敢做保证;但是需要在极短的时间内再做白盒测试,时间上更是不允许;故这个时候就就有灰盒测试的用武之地了。

  采用灰盒测试,对测试人员的技能要求,熟悉开发所用的语言,稍微了解框架和设计模式,熟悉白盒测试的覆盖方法(eg:逻辑覆盖,语句覆盖,条件覆盖,组合覆盖==),当然还得需要有阅读提测代码的权限。

  完全纯理论的讲那就是浮云了。下面几篇,我再结合实例分享一下,我做灰盒测试的一些例子吧。

分解测试任务

  做灰盒测试,最常见的的场景就是用在模块测试中,个人倾向于使用模块测试 自底向上的测试。

  首先,拿到一个测试任务(eg:该测试任务是一个新的较独立的功能),不用说,必须了解该功能要实现神马功能,以及实现原理(在对被测对象不熟悉的情况下,可以选择跟开发沟通;如果熟悉的话,自己看svn就好了),也就是对测试任务先做一个快速的分析

  然后,找到该功能的入口,可能需要借助类似流程图的方法,来迅速展开整个流程或者逻辑(这里推荐一款软件Code Visual to Flowchart,可以方便的生成出cpp的函数级别流程图);从函数入口开始一路向下分解,直至最小函数单元为止。这样就大概能生成如下的一个层次:

  从入口开始,主流程里,分别存在调用了三个分支函数,然后branch01里调用了function01;branch02里调用了function01和function02,而function02不是最小单元函数,它还调用了function03;branch03则调用了function03;故在这个例子里,最小函数单元就是function03,我们的自底而上的模块测试就从func03开始

  总结一下这部分内容:灰盒测试最主要的一个步骤,即是分析和分解测试任务,手段不限。

对最小单元函数做灰盒测试

  由于灰盒测试不需要像白盒测试一样写代码,故针对最小单元函数的灰盒测试,可以理解成“使用黑盒的手段来驱动,同时用白盒测试的方法来测试,再以白盒或者黑盒的方式来检查结果”;

  这里有一个前提,就是需要保证本次提测的这个功能模块逻辑上没有大问题,这个流程可以走通,且待测试的函数可以被执行到,不然的话,本次灰盒测试根本就没有办法做。(这里也可以借助类似windbg等调试工具来辅助测试,后面的文章再讲讲)

  可以先选择一下将采用的白盒的测试方法(eg:逻辑覆盖,语句覆盖,条件覆盖,组合覆盖==),在开始做灰盒测试之前,拜读过一篇白盒测试的文章,里面提到过一个简单路径覆盖的方法,我觉得挺简单的,就直接借用了。

  eg:拿一个简单的python demo函数来说明下分析方法,demo如下:

#----------------------------------------------------------------------
def Demo(testStr):
    """
    This is a Demo
    """
    testResult = None
    
    if (testStr == ""):
        return testResult
    elif("a" in testStr):
        testResult = 1
    else:
        testResult = 0
       

    return testResult

  首先,对这个函数做一个简单的逻辑流程图(不见得一定要画出来),图如下:

  有这么一个用于计算当前逻辑,至少需要多少个案例才可以覆盖的公式:

  圈复杂度V(G)=E-N+2,E是流图中边的数量,N是流图中结点的数量

  故:圈复杂度=8-7+2=3

  也就是说,当前至少需要3个案例才可以完成简单路径覆盖

  案例:

  case no.1 :   begin 1-> if 2-> return 7
  case no.2 :   begin 2-> if 2-> elif 3->xxxxx 4 -> else 5 -> xxxxx 6 -> return 7
  case no.3:    begin 3-> if 2-> elif 3->else 5 -> xxxxx 6 -> return 7

但是单单只对 最小单元函数做简单路径覆盖,是远远不够的。

对最小单元函数做灰盒测试

对最小单元函数做灰盒测试

  接着上回继续“最小单元函数做简单路径覆盖,是远远不够的”

  为神马呢,路径覆盖,也只能验证各种不同的条件组合,执行到的各个分支路径的代码是否都是有效的;此时可以分成三种不同的思路来继续测试:

  一、静态白盒测试

  静态白盒测试的概念我就不解释了(可以自己google下),也可以理解成是按照先前完成的路径覆盖,去对做基本代码审查。eg:是否有引用的变量未赋值,打开句柄未释放,条件判断语句是否写错,数组的引用是否超出规定的界限之外等等。

  ps:做这个测试初期不能指望能一次性发现全部BUG,不过在通过其他手段发现BUG之后,不妨都基于代码层review下,看看BUG究竟是怎么出现的,日积月累后必然会大幅提升你的代码审查能力,运用静态白盒测试,也可以发现更多以前没法发现的BUG(后面的日志会找些平时碰到的实例来分享给大家)

————————————————————————分割——————————————————————————

  二、函数级别的黑盒

  这个部分其实就相当于把 待测试的函数作为测试对象,input参数就是testcase中的“测试步骤”;而函数的返回值,则是testcase中的“实际结果”;而我们需要做的,也就是,通过黑盒的手段来制造input参数,并通过返回值来观察或者现象来判定当前的实际结果,是否与预期结果一致。

  ps:这个需要对待测函数的功能完全了解,才可以展开;使用该测试的好处是,可以发现静态白盒测试和路径覆盖未能发现的问题

————————————————————————分割——————————————————————————

  三、参照需求检查逻辑(静态黑盒)

  这个部分其实无须单独提出来,其实在执行静态白盒和函数级别的黑盒时,都可以顺带着完成了;但是很多问题,如果单纯的只跟着代码逻辑和开发的思路走,是很难暴露出来的,且需求也有可能出现纰漏。

  光写概念上的东西,没亲身执行过的话,我也会觉得很生涩,下面的日志就开始结合一些实例来扩宽下。

继续模块测试

  虽然我也不太喜欢一直写理论和概念,还是先把模块测试的流程写完吧,然后再做实例分享。

  当我们完成了最小单元函数的灰盒测试之后,即完成了下图的func03的测试:

  我们将秉承 自底向上的 模块测试流程,向上一层,把func03视为可信,对func02 和 func01做相同的测试步骤;再完成后,继续向上一层,去测试branchxx;依次类推,逐渐向上,直到完成整个main函数的测试为止,那么我们的模块测试才算是完成了。

在灰盒测试中,使用黑盒方法和思路

  从这篇开始,往后都是一些发现BUG的分享,由于项目代码是私密的,我会使用python demo下大概的场景来替代。

  黑盒手段和思路在灰盒测试一样是受用的,demo例子如下:

testList = ["a","b","c","d","e"]
#----------------------------------------------------------------------
def DemoTest(inputStr):
    """
    This is a Demo
    """
    for (i = 0;i < 4;i++):
        if inputStr == testList[i]:
            print "bingo"

  当时场景描述:

  函数外预置一个数组,该数组里存了多个字符串,我们的测试函数里有遍历数组,匹配字符串的动作

  (当然真实的场景,会比我这个demo要复杂的多)

  测试情况:

  由于看到有数组,故 会使用到黑盒测试的 边界值 和 异常值来编写测试案例

  案例01:
  inputStr = 'a'
  预期结果:bingo

  案例02:
  inputStr = 'e'
  预期结果:bingo

  案例03:
  inputStr =  'f'
  预期结果:未命中

  测试结果:
  案例01 案例03 通过
  案例02 不通过

  测试结论:
  遍历数组的边界值过小,匹配字符串时导致未能完全遍历数组

  心得:
  黑盒测试的方法和思路,一样可以在代码级别的灰盒测试中适用