一、实验原理
1、白盒测试
白盒测试也称为结构化测试或逻辑驱动测试,也就是已知产品的内部工作过程,清楚最终生成软件产品的计算机程序结构及其语句,按照程序内部的结构测试程序,测试程序内部的变量状态、逻辑结构、运行路径等,检验程序中的每条通路是否都能按预定要求正确工作,检查程序内部动作或运行是否符合设计规格要求,所有内部成分是否按规定正常进行。
白盒测试不仅可以应用在程序的单元测试,覆盖程序的语句、分支或逻辑路径,而且可以扩展到更高层次的控制流路径(如业务流程路径和数据流路径等)的覆盖。一旦将这些流程图绘制出来,就可以设计足够的测试用例来覆盖其分支、条件、条件组合或基本路径等,从而比较容易衡量测试的覆盖率,以判断测试是否充分、是否达到相应的软件产品测试要求。白盒测试的基本原则如下。
(1)在执行测试时,先考虑代码行和分支被覆盖;
(2)再考虑完成所有逻辑条件分别为真值(True)和假值(False)的测试;
(3)如果有更高的质量要求,测试对象流程图中所有独立路径至少被运行一次;
(4)检查内部数据结构,注意上下文的影响,以确保其测试的有效性。
2、逻辑覆盖测试
逻辑覆盖测试是一种白盒测试方法,目标是确保测试用例可以覆盖到程序中的所有逻辑路径。逻辑路径是程序中的一个代码分支或路径,它基于程序中的条件语句和循环语句,可以通过以下几种方法实现:
- 语句覆盖:确保测试用例可以覆盖到每个语句至少一次。
- 判定覆盖:确保测试用例可以覆盖到每个判定的两个结果,例如if语句的true和false分支。
- 条件覆盖:确保测试用例可以覆盖到每个条件的两个结果,例如if语句的条件为x<0的true和false分支。
- 判定条件覆盖:确保测试用例可以覆盖到每个判定的两个结果和每个条件的两个结果。
- 路径覆盖:确保测试用例可以覆盖到每个可能的代码路径,包括所有的条件和循环。
通过逻辑覆盖测试,测试人员可以确保程序的逻辑路径被测试用例覆盖,帮助测试人员发现程序中的逻辑错误和缺陷,例如死循环、无限递归、条件错误等,从而提高程序的质量和可靠性。逻辑覆盖测试需要测试人员具备一定的编程和代码分析能力,以便设计和执行有效的测试用例。同时逻辑覆盖测试也需要考虑程序的复杂性和覆盖率,以确保测试的完整性和有效性。
二、实验步骤
1、给出 Demo程序流程图;
2、分别以语句覆盖和判定覆盖方法设计测试用例,并写出每个测试用例的执行路径;
3、分别以条件覆盖、判定条件覆盖和条件组合覆盖方法设计测试用例,并写出每个测试用例的执行路径;
4、基于Junit实现上述5组测试用例,尽量达到全覆盖,导出测试结果报告。
三、详细设计
1、GetNum测试程序
public int getNum() {
int z;
int res;
if (x < 0) {
z = y - x;
} else {
z = y + x;
}
if (z < 10 && y > 0) {
res = z * y;
} else {
res = z * x;
}
return res;
}
2、测试数据集设计
为了便于按照不同方式测试各种情况的数据,根据程序接收的参数设计数据集格式如下:
数据集的数据结构是一个二维数组,每一行表示一个测试用例,每个用例包括三个特征:x、y和res。x、y、res均是数值型特征。具体来说,这个数据集中每个测试用例包含三个元素,可以表示为:
{x,y,res}
其中x、y是int类型,res是int类型,表示由x和y确定的返回值。
3、测试用例设计
(1)语句覆盖测试
语句覆盖要求测试用例覆盖到程序中的每个语句至少一次,即测试用例必须执行程序的每个语句,以确保程序的每个语句都能够正确运行。
由程序流程图可知,流程包含2个判断语句,并分别产生2个判断分支,因此若要实现语句覆盖,至少需要2个测试样例,设计如下:
- {1, 2, 6}(开始 → 输入x和y → 初始化 → x < 0 → z = y + x → z < 10 && y > 0 → res = z * y → return res → 结束)
- {-1, -2, 1}(开始 → 输入x和y → 初始化 → x < 0 → z = y - x → z < 10 && y > 0 → res = z * x → return res → 结束)
(2)判定覆盖测试
判定覆盖要求测试用例覆盖到程序中的每个判定的两个结果,例如if语句的true和false分支,即测试用例必须执行程序中的每个判定,并检查程序在不同情况下的行为。
由程序流程图可知,流程包含2个判断语句(x < 0,z < 10 && y > 0),要求2个判断各自至少实现1次true和false,因此若要实现语句覆盖,至少需要2个测试样例,设计如下:
- {1, 2, 6}(F,T)
- {-1, -2, 1}(T,F)
(3)条件覆盖测试
条件覆盖要求测试用例覆盖到程序中的每个条件的两个结果,例如if语句的条件为x < 0的true和false分支,即测试用例必须执行程序中的每个条件,并检查程序不同条件下的行为。
由程序流程图可知,流程包含3组条件语句(x < 0,z < 10,y > 0):
① x < 0 or x ≥ 0;
② z < 10 or z ≥ 10;
③ y > 0 or y ≤ 0;
因此若要实现条件覆盖,至少需要2个测试样例,设计如下:
- {1, 8, 72}(F,T,T)
- {-11, -1, -110}(T,F,F)
(4)判断条件覆盖测试
判定条件覆盖要求测试用例覆盖到程序中的每个判定的两个结果和每个条件的两个结果。即测试用例必须执行程序中的每个判定和每个条件,并检查程序在不同情况下的行为。
判定条件覆盖的核心是判定覆盖测试和条件覆盖测试都要满足,即程序中每条判断分支(x < 0,z < 10 && y > 0)至少实现1次true和false,同时每个条件(x < 0,z < 10,y > 0)的结果至少执行一次,因此若要实现判断条件覆盖,至少需要2个测试样例,设计如下:
- {1, 8, 72}(F,T,F,T,T)
- {-11, -1, -110}(T,F,T,F,F)
(5)路径覆盖测试
路径覆盖要求测试用例覆盖到程序中的每个可能的路径。即测试用例必须执行程序中的每个可能路径,包括所有的循环和条件路径。
由程序流程图可知,流程包含由2个判断语句(x < 0,z < 10 && y > 0)形成的2条判断分支,实际上最少需要4条路径即可经过所有程序语句,因此若要实现判断路径覆盖,至少需要4个测试样例,设计如下:
- {-1, 1, 2}(T,T)
- {-2, -1, -2}(T,F)
- {1, 1, 2}(F,T)
- {2, -1, 2}(F,F)
4、样例测试
首先按照Fixture方法编写测试方法执行前后需要进行的操作,代码如下:
@BeforeEach
public void setUp(int x, int y) {
getNum = new GetNum(x, y);
}
@AfterEach
public void tearDown() {
getNum = null;
}
public boolean verify(int expected, int actual) {
return expected == actual;
}
public String record(int x, int y, int expected, int actual, boolean testResult) {
if (testResult) {
return String.format("Pass. x:%d, y:%d, result:%s", x, y, actual);
} else {
return String.format("Fail. x:%d, y:%d, Excepted:%s, Actual:%s", x, y, expected, actual);
}
}
public void testGetNum(int x, int y, int res) {
setUp(x, y);
int actual = getNum.getNum();
boolean testResult = verify(res, actual);
System.out.println(record(x, y, res, actual, testResult));
tearDown();
}
(1)语句覆盖测试
@Test
public void test_Statements() {
GetNumTest test = new GetNumTest();
for (Object[] data : test.test_Statements) {
int x = (int) data[0];
int y = (int) data[1];
int expected = (int) data[2];
test.testGetNum(x, y, expected);
}
}
(2)判断覆盖测试
@Test
public void test_Judgement() {
GetNumTest test = new GetNumTest();
for (Object[] data : test.test_Judgement) {
int x = (int) data[0];
int y = (int) data[1];
int expected = (int) data[2];
test.testGetNum(x, y, expected);
}
}
(3)条件覆盖测试
@Test
public void test_Condition() {
GetNumTest test = new GetNumTest();
for (Object[] data : test.test_Condition) {
int x = (int) data[0];
int y = (int) data[1];
int expected = (int) data[2];
test.testGetNum(x, y, expected);
}
}
(4)判断条件覆盖测试
@Test
public void test_Judgement_Condition() {
GetNumTest test = new GetNumTest();
for (Object[] data : test.test_Judgement_Condition) {
int x = (int) data[0];
int y = (int) data[1];
int expected = (int) data[2];
test.testGetNum(x, y, expected);
}
}
(5)路径覆盖测试
@Test
public void test_Path() {
GetNumTest test = new GetNumTest();
for (Object[] data : test.test_Path) {
int x = (int) data[0];
int y = (int) data[1];
int expected = (int) data[2];
test.testGetNum(x, y, expected);
}
}
四、实验结果与分析
1、实验结果
(1)语句覆盖测试
(2)判断覆盖测试
(3)条件覆盖测试
(4)判断条件覆盖测试
(5)路径覆盖测试
2、实验分析
项目 | 统计数据 |
测试用例总数 | 12 |
测试用例执行总数 | 12 |
测试用例执行率 | 100% |
测试用例通过总数 | 12 |
测试用例通过率 | 100% |
测试用例未通过总数 | 0 |
测试用例未通过率 | 0% |
从表中得出所有测试样例全部通过,说明GetNum测试程序设计完善,通过测试。