【软件工程】黑盒测试——基于决策表分析法

实验三 黑盒测试——基于决策表分析法


图:封面

开发语言及实现平台或实验环境

在 Windows 7 或 Windows 10 操作系统上,使用 C,C++,C#或 Java 语言 及对应的开发环境(IDE)

实验目的

  1. 认识黑盒测试方法中基于决策表分析的测试方法原理
  2. 掌握黑盒测试方法中基于决策表分析的测试方法过程

实验要求

  1. 要求学生能够理解基于决策表的测试方法的相关概念、方法和过程 ~
  2. 具体为每一组,每组二——五人,根据实验题目编写出对应的 c 语言或c++语言程序,组间相互交换程序,按黑盒测试的方法进行测试。

实验原理

对编写的实验内容,采用基于决策表的测试方法进行黑盒测试。

实验材料

  1. 三角形问题
    问题描述:三角形问题接受三个整数,a、b 和 c 作为输入,用作三角
    形的边。程序的输出是由这三条边确定的三角形类型:等边三角形、等腰 三角形、不等边三角形或非三角形。
    作为输入:三角形的三条边必须满足如下条件: C1:1<=a<=100
    C2:1<=b<=100
    C3:1<=c<=100
    C4:a<b+c C5:b<a+c C6:c<a+b

  2. NextDate 函数
    问题描述:NextDate 是一个由三个变量(月份、日期和年份)的函数。
    函数返回输入日期后边的那个日期。 作为输入:变量月份、日期和年都具有整数值,满足以下条件。 C1:1<=月份<=12
    C2:1<=日期<=31
    C3:1912<=年<=2050

实验步骤

  1. 预习相关课堂和实验内容,了解测试对象。
  2. 编写实验材料源代码。
  3. 划分等价类,设计测试用例。
  4. 根据问题需要建立桩模块和驱动模块。
  5. 测试。
  6. 填写试验报告。

实验代码

三角形问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package procs;

import java.util.InputMismatchException;
import java.util.Scanner;

public class TriangleProc {

public int sideA = 0;
public int sideB = 0;
public int sideC = 0;

public TriangleProc() {

}

/**
* 函数:验证三角形边的长度范围
*
* @param side
* @return
*/
public boolean verifyLengthRange(int side) {
if ((side >= 1) && (side <= 100)) {
return true;
} else {
System.out.println(String.format("边长 %1d 不满足大于等于1且小于等于 100", side));
System.out.println("=========请重新输入=========");
return false;
}

}

/**
* 函数:验证三角形两边之和大于第三边
*
* @param triangleProc
* @return
*/
public boolean verifyFormPrinciple(TriangleProc triangleProc) {

if ((triangleProc.sideA + triangleProc.sideB <= triangleProc.sideC)
|| (triangleProc.sideA + triangleProc.sideC <= triangleProc.sideB)
|| (triangleProc.sideB + triangleProc.sideC <= triangleProc.sideA)) {
System.out.println(String.format("边长组合为 %1d %2d %3d不满足两边之和大于第三边,是非三角形",
triangleProc.sideA, triangleProc.sideB, triangleProc.sideC));
System.out.println("=========请重新输入=========");
return false;
} else {
System.out.println("边长组合满足两边之和大于第三边");
return true;
}
}


public String judgeTriangleType(TriangleProc triangleProc) {
if (triangleProc.sideA == triangleProc.sideB || triangleProc.sideA == triangleProc.sideC
|| triangleProc.sideB == triangleProc.sideC) {
if (triangleProc.sideA == triangleProc.sideB && triangleProc.sideB == triangleProc.sideC) {
String triangleType = "等边三角形";
return triangleType;
} else {
String triangleType = "等腰三角形";
return triangleType;
}
} else {
String triangleType = "非等边三角形";
return triangleType;
}
}

public void inputEdge(Scanner scanner, TriangleProc triangleProc) {
int tempSideA = 0;
int tempSideB = 0;
int tempSideC = 0;

// System.out.println("Welcome to triangle judge program!!!d");
System.out.println("Please input the side A");

if (scanner.hasNext()) {
tempSideA = scanner.nextInt();
triangleProc.sideA = tempSideA;
System.out.println(String.format("Edge A is %1d. Please input side B", tempSideA));
tempSideB = scanner.nextInt();
triangleProc.sideB = tempSideB;
System.out.println(String.format("Edge B is %1d. Please input side C", tempSideB));
tempSideC = scanner.nextInt();
triangleProc.sideC = tempSideC;
System.out.println(String.format("Edge C is %1d. The input is finished\n\n", tempSideC));

}

boolean stopFlag = false;

while (!stopFlag) {
if (verifyLengthRange(triangleProc.sideA) && verifyLengthRange(triangleProc.sideB)
&& verifyLengthRange(triangleProc.sideC)) {
if (verifyFormPrinciple(triangleProc)) {
stopFlag = true;
String triangleType = judgeTriangleType(triangleProc);
System.out.println(String.format("你输入的三角形类型为:%1s", triangleType));
System.out.println("========The End========");
} else {
inputEdge(scanner, triangleProc);
}

} else {
// 边长不满足两个条件,重新输入
inputEdge(scanner, triangleProc);
}
}
}


/**
* 主函数,同时也是测试函数
*
* @param args
*/
public static void main(String[] args) {
TriangleProc triangleProc = new TriangleProc();
Scanner scanner = new Scanner(System.in);
triangleProc.inputEdge(scanner, triangleProc);

}

}

NextDate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package procs;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Scanner;

public class NextDate {
public int year = 0;
public int month = 0;
public int day = 0;


/**
* 函数:根据指定的日期,计算下一天
* 注意:应该在进行日期检查之后再调用
* @param year
* @param month
* @param day
* @return
*/
public String nextDateFunction(int year, int month, int day){

Calendar calendar = Calendar.getInstance();

String yearString = Integer.toString(year);
String monthString = Integer.toString(month);
String dayString = Integer.toString(day);

String stringDate = yearString + '-' + monthString + '-' + dayString;

Date date = null;
try {
date = new SimpleDateFormat("yyyy-MM-dd").parse(stringDate);
} catch (ParseException e) {
e.printStackTrace();
}

calendar.setTime(date);

int dayBefore = calendar.get(Calendar.DATE);
calendar.set(Calendar.DATE, dayBefore + 1);

String dayAfter = new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime());
return dayAfter;

}


/**
* 函数:日期检查函数
* 检查指定的日期是否符合要求
* @param scanner
* @param nextDate
*/
public void dateCheckFunction(Scanner scanner, NextDate nextDate) {
System.out.println("Please Input Your Specific Day");

do {
System.out.println("Please Input the Year(1912 <= year <= 2050): ");
nextDate.year = scanner.nextInt();
} while (nextDate.year < 1912 || nextDate.year > 2050);

do {
System.out.println("Please Input the Month(1 <= month <= 12): ");
nextDate.month = scanner.nextInt();
} while (nextDate.month < 1 || nextDate.month > 12);

do {
System.out.println("Please Input the Day(1 <= day <= 31): ");
nextDate.day = scanner.nextInt();
} while (nextDate.day < 1 || nextDate.day > 31);

}

public static void main(String[] args) {

Scanner scanner = new Scanner(System.in);

NextDate nextDate = new NextDate();

// 检查输入的日期是否符合要求
nextDate.dateCheckFunction(scanner, nextDate);

// 根据nextDateFunction函数获取后一天的日期
String dayAfter = nextDate.nextDateFunction(nextDate.year, nextDate.month, nextDate.day);

System.out.println("The day after is " + dayAfter);


}

}

佣金问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package procs;

import java.util.Scanner;


public class GunSale {
// 机枪
int part1 = 0;
int part1Price = 45;
// 枪托
int part2 = 0;
int part2Price = 30;
// 枪管
int part3 = 0;
int part3Price = 25;

// 销售数量总和
int amountSum = 0;
// 总销售额
int priceSum = 0;
// 佣金总和
double commissionSum = 0.00f;



// 枪部件输入、检查函数
private void amountInputAndCheck(Scanner scanner, GunSale gunSale) {
System.out.println("Please input the amount of a gun's every part");
System.out.println("----------------------------------------------");
System.out.println("------------机枪----枪托----枪管------------");
/**
* 输入的数量赋值给gunSale对象,且对其销售数量进行检查
*/
do {
System.out.printf("请输入机枪销售额:");
gunSale.part1 = scanner.nextInt();
} while (gunSale.part1 < 1 || gunSale.part1 > 70);

do {
System.out.printf("请输入枪托销售额:");
gunSale.part2 = scanner.nextInt();
} while (gunSale.part2 < 1 || gunSale.part1 > 80);

do {
System.out.printf("请输入枪管:");
gunSale.part3 = scanner.nextInt();
} while (gunSale.part3 < 1 || gunSale.part1 > 90);

}

// 销售数目总和
private int amountSumFunction(GunSale gunSale) {
int amount = gunSale.part1 + gunSale.part2 + gunSale.part3;
gunSale.amountSum = amountSum;
return amount;
}

// 总销售额
private int priceSumFunction(GunSale gunSale) {
int priceSum = gunSale.part1 * gunSale.part1Price + gunSale.part2 * gunSale.part2Price
+ gunSale.part3 * gunSale.part3Price;
gunSale.priceSum = priceSum;
return priceSum;
}

private double commissionSum(GunSale gunSale) {
double commissionSum = 0.00f;

if (gunSale.priceSum <= 1000) {
commissionSum = gunSale.priceSum * 0.1;
} else if (gunSale.priceSum <= 1800) {
commissionSum = (gunSale.priceSum - 1000) * 0.15 + 100;
} else {
commissionSum = (gunSale.priceSum - 1800) * 0.20 + 220;
}

gunSale.commissionSum = commissionSum;
return commissionSum;
}


public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
GunSale gunSale = new GunSale();
gunSale.amountInputAndCheck(scanner, gunSale);
int amountSum = gunSale.amountSumFunction(gunSale);
int priceSum = gunSale.priceSumFunction(gunSale);
double commissionSum = gunSale.commissionSum(gunSale);
System.out.println(String.format("=====销售总数是:%1d, 总销售额:%2d,佣金总和:%3f====="
, amountSum, priceSum, commissionSum));
System.out.println("----------------------------------------------");
}

}

测试案例

三角形问题:测试用例设计

1.第一步,分析实验实验,确定条件桩和动作桩
| 条件桩 | 行动桩(不与条件桩一一对应) |
| ———- | —— |
| C1: a<b+c? | 非三角形 |
| C2: b<a+c? | 不等边三角形 |
| C3: c<a+b? | 等腰三角形 |
| C4: a=b? | 等边三角形 |
| C5: a=c? | 不可能 |
| C6: b=c? | |

2.确定规则的个数

由于该问题中为有限条目决策表(二值条件,真/假),所以规则的数目为:2^6 = 64(条)

3.填入条件项

条件
C1: a<b+c? F F F F F F F F
C2: b<a+c? F F F F F F F F
C3: c<a+b? F F F F F F F F
C4: a=b? F F F T T T F T
C5: a=c? F F T F F T T T
C6: b=c? F T F F T F T T

4.填入动作项

条件 1 2 3 4 5 6 7 8
C1: a<b+c? F F F F F F F F
C2: b<a+c? F F F F F F F F
C3: c<a+b? F F F F F F F F
C4: a=b? F F F T T T F T
C5: a=c? F F T F F T T T
C6: b=c? F T F F T F T T
A1: 非三角形 1 1 1 1 1 1 1 1
A2: 不等边三角形
A3: 等腰三角形
A4: 等边三角形
A5: 不可能

5.合并相似规则

从上表中可以发现,测试用例1-8,由于C1条件为false,不管下面的条件项真值情况,动作项都为A1。此时C2-C6为不相关条目,因此可以被合并。以下表格为合并后的结果:
| 条件 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| —————- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- |
| C1: a<b+c? | F | T | T | T | T | T | T | T | T | T | T |
| C2: b<a+c? | - | F | T | T | T | T | T | T | T | T | T |
| C3: c<a+b? | - | - | F | T | T | T | T | T | T | T | T |
| C4: a=b? | - | - | - | F | T | T | F | T | F | F | T |
| C5: a=c? | - | - | - | F | F | T | T | F | T | F | T |
| C6: b=c? | - | - | - | F | F | F | T | T | F | T | T |
| A1: 非三角形 | 1 | 1 | 1 | | | | | | | | |
| A2: 不等边三角形 | | | | 1 | | | | | | | |
| A3: 等腰三角形 | | | | | 1 | | | | 1 | 1 | |
| A4: 等边三角形 | | | | | | | | | | | 1 |
| A5: 不可能 | | | | | | 1 | 1 | 1 | | | |

合并后即为最终决策表。

  1. 得到测试用例
    用例ID a b c 预期输出
    1 5 2 3 非三角形
    2 2 5 3 非三角形
    3 2 3 5 非三角形
    4 3 4 5 不等边三角形
    5 3 3 4 等腰三角形
    6 ? ? ? 不可能
    7 ? ? ? 不可能
    8 ? ? ? 不可能
    9 3 4 3 等腰三角形
    10 4 3 3 等腰三角形
    11 3 3 3 等边三角形

NextDate:测试用例设计

  1. 分析案例,确定条件桩和行动桩

    又因为案例中直接将边界条件转化为条件桩的话,会缺漏的很多条件,因此,更加细分并且使用等价类归类。

    等价类
    M1 = {月份:每月有30天}
    M2 = {月份:每月有31天,12月除外}
    M3 = {月份:此月是12月}
    M4 = {月份:此月是2月}
    D1 = {日期:1<=日期<=27}
    D2 = {日期:日期 = 28}
    D3 = {日期:日期 = 29}
    D4 = {日期:日期 = 30}
    D5 = {日期:日期 = 31}
    Y1 = {年:年是闰年}
    Y2 = {年:年是平年}
条件桩
C1:月份在{M1, M2, M3, M4}中之一
C2:日期在{D1, D2, D3, D4, D5}中之一
C3:年份在{Y1, Y2}中之一
动作桩
A1:不可能
A2:日增1
A3:日复位
A4:月增1
A5:月复位
A6:年增1

2.确定规则个数

4*5*2 = 40

3.填入条件项

条件 1 2 3 4 5 6 7 8
C1:月份在{M1, M2, M3, M4}中之一 M1 M1 M1 M1 M1 M2 M2 M2
C2:日期在{D1, D2, D3, D4, D5}中之一 D1 D2 D3 D4 D5 D1 D2 D3
C3:年份在{Y1, Y2}中之一 Y1 Y1 Y1 Y1 Y1 Y1 Y1 Y1
A1:不可能
A2:日增1
A3:日复位
A4:月增1
A5:月复位
A6:年增1

4.填入动作项

条件 1 2 3 4 5 6 7 8
C1:月份在{M1, M2, M3, M4}中之一 M1 M1 M1 M1 M1 M2 M2 M2
C2:日期在{D1, D2, D3, D4, D5}中之一 D1 D2 D3 D4 D5 D1 D2 D3
C3:年份在{Y1, Y2}中之一 Y1 Y1 Y1 Y1 Y1 Y1 Y1 Y1
A1:不可能 1
A2:日增1 1 1 1 1 1 1
A3:日复位 1
A4:月增1 1
A5:月复位
A6:年增1

5.合并相似规则

条件 1 2 3 4 5 6 7 8
C1:月份在{M1, M2, M3, M4}中之一 M1 M1 M1 M1 M1 M2 M2 M2
C2:日期在{D1, D2, D3, D4, D5}中之一 D1 D2 D3 D4 D5 D1 D2 D3
C3:年份在{Y1, Y2}中之一 - - - - - - - -
A1:不可能 1
A2:日增1 1 1 1 1 1 1
A3:日复位 1
A4:月增1 1
A5:月复位
A6:年增1

6.得出测试用例

用例ID 月份 日期 年份 预计输出
1-3 4 12 2001 2001年4月12日
4 4 30 2001 2001年5月1日
5 4 31 2001 不可能
6-9 1 15 2001 2001年1月16日
10 1 31 2001 2001年2月1日
11-14 12 15 2001 2001年12月16日
15 12 31 2001 2002年1月1日
16 2 15 2001 2001年2月16日
17 2 31 2004 2004年2月29日

测试代码及结果分析

三角形问题:测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package exps;

import procs.TriangleProc;

public class TriangleTesting_third {

public void checkShape(int sideA, int sideB, int sideC) {
TriangleProc triangleProc = new TriangleProc();

triangleProc.sideA = sideA;
triangleProc.sideB = sideB;
triangleProc.sideC = sideC;

triangleProc.verifyLengthRange(triangleProc.sideA);
triangleProc.verifyLengthRange(triangleProc.sideB);
triangleProc.verifyLengthRange(triangleProc.sideC);

triangleProc.verifyFormPrinciple(triangleProc);

System.out.println("三角形类型为:" + triangleProc.judgeTriangleType(triangleProc));
}

public static void main(String[] args) {

TriangleTesting_third triangleTesting_third = new TriangleTesting_third();

triangleTesting_third.checkShape(5, 2, 3);
triangleTesting_third.checkShape(2, 5, 3);
triangleTesting_third.checkShape(2, 3, 5);
triangleTesting_third.checkShape(3, 4, 5);
triangleTesting_third.checkShape(3, 3, 4);
System.out.println("=====不可能=====");
System.out.println("=====不可能=====");
System.out.println("=====不可能=====");
triangleTesting_third.checkShape(3, 4, 3);
triangleTesting_third.checkShape(4, 3, 3);
triangleTesting_third.checkShape(3, 3, 3);

}

}

三角形问题:测试结果

符合实验预期

NextDate:测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package exps;

import procs.NextDate;

import java.util.Scanner;

public class NextDateTesting_third {

/**
* 驱动模块
* 函数:dateCheckFunction
*
* @param nextDate
*/
public boolean dateCheckFunction(NextDate nextDate, int year, int month, int day) {

boolean flag = true;

if (year >= 1912 && year <= 2050) {
nextDate.year = year;
} else {
System.out.println("The YEAR is out of range");
flag = false;
}

if (month >= 1 && month <= 12) {
nextDate.month = month;
} else {
System.out.println("The MONTH is out of range");
flag = false;

}

if (day >= 1 && day <= 31) {
nextDate.day = day;
} else {
System.out.println("The DAY is out of range");
flag = false;

}

return flag;

}

public void checkNextDate(int year, int month, int day) {
NextDate nextDate = new NextDate();
NextDateTesting_two nextDateTesting_two = new NextDateTesting_two();

// 检查输入的日期是否符合要求
if (nextDateTesting_two.dateCheckFunction(nextDate, year, month, day)) {
// 根据nextDateFunction函数获取后一天的日期
String dayAfter = nextDate.nextDateFunction(nextDate.year,
nextDate.month, nextDate.day);

System.out.println("The day after is " + dayAfter);
}


}


public static void main(String[] args) {

NextDateTesting_third nextDateTesting_third = new NextDateTesting_third();

nextDateTesting_third.checkNextDate(2001, 4, 12);
nextDateTesting_third.checkNextDate(2001, 4, 30);
System.out.println("将超出的日期当作下月的日期增位");
nextDateTesting_third.checkNextDate(2001, 4, 31);
nextDateTesting_third.checkNextDate(2001, 1, 15);
nextDateTesting_third.checkNextDate(2001, 1, 31);
nextDateTesting_third.checkNextDate(2001, 12, 15);
nextDateTesting_third.checkNextDate(2001, 2, 15);
nextDateTesting_third.checkNextDate(2004, 2, 31);


}
}

NextDate:测试结果

以上是测试结果,符合实验预期

实验总结

基于决策表的黑盒测试一般测试步骤

  1. 列出所有的条件桩和动作桩
  2. 确定规则的个数
  3. 填入条件项(有限条件决策表/扩展条目决策表)
  4. 填入动作项
  5. 合并相似规则(不是必须的)
  6. 得到测试用例

决策表的适用范围

通常适用于要产生大量决策的情况,或者输入变量之间存在重要逻辑关系。

1.决策表测试法适用于具有以下特征的应用程序

  • 程序的 if then else 逻辑突出
  • 输入变量之间存在逻辑关系
  • 计算过程涉及输入变量子集的处理
  • 输入与输出之间存在因果关系
  • 圈复杂度较高的应用

2.适用于使用决策表设计测试用例的条件

  • 规格说明以决策表形式给出,或较容易转换为决策表
  • 条件的排列顺序不会也不应影响执行的操作
  • 规则的排列顺序不会也不应影响执行的操作
  • 当某一规则的条件已经满足,并确定要执行的操作后,不必检验别的规则
  • 如果某一规则的条件要执行多个操作,这些操作的执行顺序无关紧要