这里主要发表单元测试领域的原创技术文章,以及Visual Unit相关资讯,欢迎您光临!

发布新日志

  • 没有输入参数的接口函数如何设计测试用例?

    2008-10-21 17:32:09

    对于函数测试来说,一个用例,就是设定输入,执行程序,判断输出是否符合预期。
    可能输入包括:参数、需读的成员变量、需读的全局变量、内部输入(调用子函数获得的输入);
    可能输出包括:返回值、输出参数、被写的成员变量、被写的全局变量,内部输出(在程序执行过程中判断的中间输出)、动作(例如需判断程序在某种输入下是否调用了某个函数)。
    简单来说,输入就是程序执行前或执行过程中读取的外部数据,输出就是程序所改写的数据。
    了解了这些,就不会对没有参数、没有返回值如何测试产生疑问了。

    测试没有参数的函数,它可能还有别的输入,例如全局变量,成员变量,或调用子函数获得的输入(这个要使用工具才能做到),只要函数需读取的,都应该设定初始值,如果完全没有,没有输入也是一种输入,照样测试就是了。 同样道理,输出也不仅仅是返回值,没有返回值还可能修改了全局变量什么的,这些也是要判断的输出。

    但是,单元测试应该测试哪些比较复杂的程序,而不是只测试接口。对于只是读写一两个数据的接口,没什么好测试的,例如“DWORD GetInterfaceVersion ();//获取解码器版本号”,应该只是读取一个全局变量并返回,没有什么测试意义,要测的话,先设定那个全局变量的值,也一样测试,例如:
    输入:SetInterfaceVersion (1234); //调用其他函数完成初始化,这个是外部输入,不是内部输入。
    输出:ASSERT(GetInterfaceVersion () == 1234);
    不过这样做没什么意义。

    更具体的单元测试技术,建议看一下视频教程《单元测试讲座》,下载地址:http://www.kailesoft.cn/download/

    以上是对这个帖子的回复,类似问题比较多见。http://www.51testing.com/spacecp.php?action=spaceblogs&op=add&openwindow=1

  • 实现完整测试的思路和方法

    2007-08-14 22:52:52

    这里提出用“三步法”尽可能实现完整测试:

    第一步:基本功能测试
    程序的功能是人为的规定,工具不可能自动了解,因此,针对基本功能的测试用例需要人工来建立,这是无可躲避的。根据程序的设计要求,基本功能用例通常不难设计,把程序功能细化、明确化,列成“什么输入,应产生什么输出”的形式,就是测试用例。程序员准备编码时和编码过程中,是建立基本功能用例的最佳时机,为什么呢?因为程序员编码之前和编码过程中,一定要弄明白程序的功能,也就是要想清楚“会有哪些输入?某种输入时程序应该做什么?产生什么结果?”,这里,“哪些输入”就是指有哪些等价类,产生的“结果”就是输出,从编码的角度来看,这些就是程序的功能点,从测试的角度来看,这些就是现成的用例。如果有详细设计文档,那么测试人员可以根据文档来设计用例,否则最好由程序员建立基本功能用例。这一步可视为“黑盒方法”。

    第二步:用白盒方法找出遗漏用例
        正因为程序功能是人为的规定,“黑盒方法”很难衡量完整性,而“白盒方法”恰恰具有易于衡量测试完整性的优点,两者可以很好互补,请看下面的示例代码:

        void Func(int* p)
        {
            if(p)
            {
                *p = 0;
            }
            else
            {
                return;
            }

        }

        参数p是一个指针,测试时当然要将空指针作为一个等价类,如果漏了这个等价类,会怎么样呢?分支覆盖会不完整:else分支未覆盖。从这个例子可以看出,未覆盖的逻辑单位通常对应未测试的等价类,因此,白盒覆盖可以衡量等价类是否完整并可帮助找出遗漏的用例。

      “白盒方法”用逻辑覆盖率来衡量测试的完整性。逻辑单位主要有:语句、分支、条件、条件值、条件值组合,路径。语句覆盖就是覆盖所有的语句,其他类推。还有一种判定条件覆盖,其实是分支覆盖与条件覆盖的组合。跟条件有关的覆盖就有三种:条件覆盖是指覆盖所有的条件表达式,即所有的条件表达式都至少计算一次,不考虑计算结果;条件值覆盖是指覆盖条件的所有可能取值,即每个条件的取真值和取假值都要至少计算一次;条件值组合覆盖是指覆盖所有条件取值的所有可能组合。与条件直接有关的错误主要是逻辑操作符错误,例如:||写成&&,漏了写!什么的,采用分支覆盖与条件覆盖的组合,基本上可以发现这些错误,另一方面,条件值覆盖与条件值组合覆盖往往需要大量的测试用例,因此,这两种覆盖的效费比偏低。基于以上理由,这里提出采用语句、条件、分支、路径覆盖的组合来衡量测试完整性和找出遗漏用例。

    第三步:用自动用例捕捉漏网之鱼
        还是上面的例子,假如程序员完全忘了有空指针这回事,把代码写成这样:
        void Func(int* p)
        {
                *p = 0;
        }

        由于判断p是否为空指针的代码不存在,白盒覆盖当然不会提示“某某代码或某某分支未覆盖”,因此,白盒覆盖不能发现“程序员未处理某些特殊输入”形成的错误,即使达到了无与伦比的白盒覆盖率,仍然不能保证找出所有等价类。
        程序员会忘记处理哪些输入呢?常见的输入一般是不会记的,否则程序的起码功能都未实现,容易忘记的是一些"偏僻"的输入,例如,空指针、空字符串、很大的数、很小的数、合法取值边界附近的值等等,从输入的角度来看,这些特殊值通常跟数据类型有关,从程序的行为来看,这些特殊输入常常会导致崩溃、产生异常,或超时,即具有行为特征,正好是自动用例可以发现的,因此,可以利用自动用例来捕捉“程序员未处理某些特殊输入”形成的错误。这就是“三步法”中的第三步。
  • 什么叫好用例?

    2007-08-13 22:52:57

    什么叫好用例?被奉为经典的标准是:发现错误可能性大的用例就是好用例,发现至今未被发现的错误的用例就是成功的用例。

    对此,有网友评论说:这个标准就像傻子吃烧饼,连吃五个不饱,吃完第六个终于饱了,于是说:早知道吃了第六个就会饱,何必吃前面五个?

    没错,设计用例时你怎么知道哪个用例发现错误的可能性大?要是代码中根本就没错误呢?

    好用例应该是一个集合,就像傻子吃的六个烧饼是一个整体,一起吃下去才会饱,而不是找出一个“好”烧饼,吃一个就饱。

    例如我们测试一个函数,如果用例集覆盖了该函数的所有等价类,那么,测试就是完整的,就是好用例和成功用例,衡量用例质量的标准跟是否发现错误无关。

    举个例子,如果把程序视为一个池塘,错误视为塘中的鱼,那么,我们建立用例就相当于织网,这个网要能覆盖整个池塘,如果做到了这一点,那么一网下去,只要池塘里有鱼,就一定能捞上来,如果没有捞上鱼,可以认为塘中没有鱼。塘中是否有鱼,并不是评价网的质量的标准。

    那么,如何衡量完整性和实现完整的测试呢?今天晚了,且听下回分解。

  • 单元测试用例概述

    2007-08-12 23:16:53

    测试的目的是检查程序的行为是否符合设计规格,程序的行为就是某种输入时会产生什么输出,因此,一个典型的测试用例完成以下工作:设定输入数据、执行程序、验证输出是否符合预期。

    函数的输入数据一般包括:
    A、参数;
    B、成员变量,只考虑函数需要读取的成员变量;
    C、全局变量,只考虑函数需要读取的全局变量;
    以上三项,当涉及到复杂数据类型时,只考虑函数需要读取的域,例如,一个结构对象,有十个域,而函数只读取其中一个域,则不必考虑其他九个域。
    D、其他数据,如函数需要读取文件或数据库中的数据,则要先在文件或数据库中设置好这些数据。

    显然,所有可能输入都进行测试,既不可能也无意义,我们应该用一定的规则选择有代表性的数据作为输入。输入可分为三大类:正常输入,边界输入,非法输入,每大类还可再分为若干小类,划分小类的依据是:同一小类中每个数据都具有等价的测试效果,也就是说,小类中取任取一个数据作为输入,如果测试通过,可以肯定同小类的其他输入也可以测试通过,这就是平常说的“等价类法”。

    正常输入

    例如字符串的Trim函数,功能是将字符串前后的空格去除,那么正常的输入可以有四类:
    前面有空格;
    后面有空格;
    前后均有空格;
    前后均无空格。

    边界输入
    上例中空字符串可以看作是边界输入。
    再如一个表示年龄的参数,它的有效范围是0-100,那么边界输入有两个:0和100。

    非正常输入
    垃圾数据或使代码不能完成正常功能的数据,如一个文件操作的函数,非正常输入有这么几类:
    文件不存在;
    目录不存在;
    文件正在被其他程序打开;
    权限错误。

    预期输出
    一个完整的测试用例应该有预期输出,预期输出就是程序运行后的预期结果,通常表现在对某些数据的修改,即预期输出要自动判断程序所改写的数据的结果值是否符合预期。程序可能修改的数据包括:
    A、返回值;
    B、输出参数;
    C、成员变量,只考虑函数所改写的成员变量;
    D、全局变量,只考虑函数所改写的全局变量;
    以上四项,当涉及到复杂数据类型时,只考虑函数所改写的域,例如,一个结构对象,有十个域,而函数只改写了其中一个域,则不必考虑其他九个域。
    E、其他数据,如函数改写文件或数据库中的数据,也是一种输出,不过通常难于自动判断是否符合预期,可用人工查看来代替。
Open Toolbar