1.软件测试覆盖原理与JUnit介绍

发布于 2022年 01月 03日 20:42

大家好,本节我们来学习一下如何用 JUint 实现对一段程序的逻辑覆盖测试。在此学习过程中,我们将了解有哪些逻辑覆盖测试策略,这些逻辑覆盖测试策略间的强弱关系,以及根据逻辑覆盖测试策略编写测试脚本最终完成单元测试。

首先我们来了解一下JUnit。

这是官网对它的一段描述,JUnit 是一个开源的Java单元测试框架,是单元测试框架体系结构xUnit的一个实例。通过JUnit提供的API可以编写出测试结果明确、可重用的单元测试脚本。

目前JUnit的发布版本是4.12,下一代是JUnit5,JUnit5在运行时需要Java8,但是JUnit5的设计上,使它仍然可以测试使用以前版本JDK编译的代码。

JUnit在1997年,由 Erich Gamma 和 Kent Beck 开发完成。其中Erich G amma 是 GOF(Gang of Four) 编写《设计模式》的四人之一;Kent Beck 则在 极限编程(XP) 中有重要的贡献。

“麻雀虽小,五脏俱全”,JUnit设计的非常小巧,但是功能却十分强大。作为开源项目,JUnit托管在GitHub上。

【ppt切换#5】接下来我们看一下待测试的程序:

这段Java程序是一个getNmb的方法,它有两个int型的参数x和y,有一个int型的返回值,这段程序并不复杂,主要逻辑由两个if-else语句构成,一共涉及五个语句块和两个分支。

接下来,我们要用 JUint 实现对该程序的逻辑覆盖测试。

具体覆盖策略包括:语句覆盖、判断覆盖、条件覆盖、判断-条件覆盖、条件组合覆盖和路径覆盖。

接下来,我们先对这段程序的流程进行分析。

这是我们分析后绘制出来的流程图。

从流程图上,我们可以直观的看出,有2个判断框,有5个语句块。其中a、b、c、d、e、f是控制流上若干程序点。

我们先来设计语句覆盖的测试用例:

语句覆盖的含义是在测试时,首先设计若干个测试用例,然后运行被测程序使程序中的每个可执行语句至少执行一次。

根据这个测试策略,我们设计了两个测试用例。它们分别覆盖了1、3、5和2、4、5。我们程序中总共有5个可执行语句。

来看一下动画效果:

测试用例1走的是abdf这条路径,覆盖了1、3、5语句块;测试用例2走的是acef路径,覆盖了2、4、5语句块。所以也就满足了语句覆盖的测试策略。每个可执行语句都至少执行一次。

接下来我们设计判定覆盖的测试用例:

判定覆盖指的是设计若干个测试用例,运行被测试程序,使得程序中每个取真分支和取假分支至少经历一次,即判断的真假值均曾被满足。

待测程序中总共有2个if-else语句,每个if-else语句有2个判定分支,取值分别为真、假,真、假。理想的情况下,理想的情况下,“1真2假”“1假2真”搭配,只需要设计两个测试用例即可以满足判定覆盖。

事实也是如此,我们据此分析设计了测试用例CASE3、CASE4。

来看一下动画效果:

测试用例三的分支判定结果为“真 假”,测试用例四的分支判定结果为“假 真”。

同时,我们还发现这两组测试用例不仅满足了判定覆盖同时还做到了语句覆盖。5个语句也均被覆盖到。这就说明判定覆盖强于语句覆盖,这个结论是成立的。

说完了判定覆盖和语句覆盖,我们再来看看条件覆盖。

条件覆盖是指设计若干测试用例,执行测试程序,使每个判断中每个条件的可能取值至少满足一次。首先要理清的是,“条件”和“判定”不同!“判定”在流程图上很明显,就是一个菱形的判断框,引出分叉。

判定覆盖关注的是,每个判定分支“出来”的时候,取真值还是取假值都要覆盖到;而条件覆盖更加关注判定中条件的内容和条件的组合。一个判定可能包含多个判断条件,有时一个条件构成一个判定。

在被测程序中的第二个判定z>10 && y>0就是两个条件的组合,在条件覆盖中需要将它们拆下来,并依次覆盖。

所以我们设计了3个测试用例。

从测试用例的设计上,TTT、FFT、FTF确实满足了条件覆盖。但我们很好奇,条件覆盖跟语句覆盖和判定覆盖谁强呢?

我们先来观察一下条件覆盖的动画效果:

CASE5的路径abdf,CASE6和CASE7经过的路径都是acef。

“条件覆盖”通常比“判定覆盖”强,因为它使一个判定中的每一个条件都取到了两个不同的结果,而判定覆盖则不保证这一点。

同样,从“判定覆盖”的角度考虑,你的测试用例取到了判定中每个条件的不同结果,但有可能在判定出来的时候,却只取到了真值或只取到了假值。打个比方,一个判定中有两个条件,两个条件间是与的关系,设计的测试用例,使两个条件的取值分别为“真 假”和“假 真”,与运算之后,都是假值。所以有的时候,条件覆盖并不满足判定覆盖。

需要注意的是:

满足条件覆盖,不一定就能满足判定覆盖!

满足条件覆盖,甚至于也不一定能满足语句覆盖!

接下来说说判定-条件覆盖:

条件覆盖并不比判定覆盖强,两者只是关注点不同,可以把条件覆盖和判定覆盖结合起来使用,这被称之为“判定-条件覆盖”,它的含义是指:设计足够多的测试用例,使得判定表达式中每个条件的真/假取值至少都出现一次,并且每个判定表达式自身的真/假取值也都要至少出现一次。

那么,我们就根据这个覆盖策略,设计测试用例了。

左边是框出的是判定,右边是条件,显然判定的真假取值每个至少出现了一次,条件的每个真假取值也都至少出现了一次。

我们来看一下它的动画效果:

动画中,CASE10和CASE11的路径重合,而且在路经第二个判定时,没有直观地看出判定中条件的取值情况。实际上,可以从计算机生成的指令角度重新审视。

判定/条件覆盖”似乎是比较合理的,但事实并非如此,因为大多数计算机不能用一条指令对多个条件作出判定,而必须将源程序中对多个条件的判定分解成几个简单判定,所以较彻底的测试应使每一个简单判定都真正取到各种可能的结果。

右图是由编译系统产生的的目标程序流程图。

Java在编译成字节码后,一个复杂的判定z>10&&y=0被分解为z>10和y=0两个简单的判定,注意右图中的判断框。

我们发现当z>10取假值时,没有再去判断y>0的取值的真假情况。就这可能出现前一个条件满足时,而后一个情况存在错误而没有执行、没有被发现的情形。这也是程序编程语言中&&与||的“短路”效果,前一个条件对后一个条件产生抑制的效果。

从这个角度考量判断条件覆盖,它并不完善,它看起来使所有条件取到所有可能的值,但并不能检查到那种程度。

而力求检查到更细致程度的就是条件组合覆盖了。

条件组合覆盖是指设计足够多的测试用例,使得每个判定中条件的各种可能组合都至少出现一次。满足“条件组合覆盖”的测试用例是一定满足“判定覆盖”、“条件覆盖”和“判定-条件覆盖”的。

为实现条件组合覆盖,我们设计了4个测试用例。

来看看条件组合覆盖的动画效果。

这里的流程框图,正是源程序编译之后生成的指令,将复杂判定分解成简单判定的流程图。所以我们可以直观的观察到更加细致的覆盖效果。

最后,我们讲讲逻辑覆盖测试中,最强的路径覆盖。

路径覆盖指得是设计足够多的测试用例,要求覆盖程序中所有可能的路径。

同样我们设计了四个测试用例。

我们可以看一下,之前设计的条件组合覆盖、判定-条件覆盖的测试用例集,它们都没有覆盖到全部的路径。条件组合覆盖只覆盖到abdf、acef两条路径,判定-条件覆盖同样也只覆盖到两个。

所以我们从路径的角度,进行覆盖。这里的测试用例集{CASE3,CASE4,CASE5,CASE6}由之前设计的测试用例构成。

路径覆盖的效果。

CASE3-abef、CASE4-acdf、CASE5-abdf、CASE6-acef。

下面我们进入 JUnit,编写测试脚本,实现对代码的测试。

测试环境是JDK1.8、eclipse Neon4.6

推荐文章