7.2三角形问题的测试用例
三角形问题的决策表:6个条件、5个行动,11条规则
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? | - | - | - | T | T | F | T | F | F | F | F |
C5:a=c? | - | - | - | T | F | T | F | T | T | F | F |
C6:b=c? | - | - | - | T | T | T | F | T | F | T | F |
A1:非三角形 | × | × | × | ||||||||
A2:不等边三角形 | × | ||||||||||
A3:等腰三角形 | × | × | × | ||||||||
A4:等边三角形 | × | ||||||||||
A5:不可能 | × | × | × |
由此得到11个测试用例,其中3个不可用,剩余8个测试用例,用例列表如下:
用例ID | a | b | c | 预期输出 |
DT1 | 4 | 1 | 2 | 非三角形 |
DT2 | 1 | 4 | 2 | 非三角形 |
DT3 | 1 | 2 | 4 | 非三角形 |
DT4 | 5 | 5 | 5 | 等边三角形 |
DT5 | ? | ? | ? | 不可能 |
DT6 | ? | ? | ? | 不可能 |
DT7 | 2 | 2 | 3 | 等腰三角形 |
DT8 | ? | ? | ? | 不可能 |
DT9 | 2 | 3 | 2 | 等腰三角形 |
DT10 | 3 | 2 | 2 | 等腰三角形 |
DT11 | 3 | 4 | 5 | 不等边三角形 |
7.3 NextDate问题测试用例
NextDate问题比三角形问题更适合于使用决策表来解决,是因为它可以说明输入定义域中的依赖性问题,使得这个例子成为基于决策表测试的一个完美例子,因为决策表可以突出这种依赖关系。第6章发现等价类的限制之一是从等价类中随意地选取输入值,会产生"很奇怪"的测试用例,比如找出1812年6月31日的下一天。问题的根源是假设变量都是独立的。如果变量确实是独立的,使用笛卡尔积是有意义的,如果变量之间在输入定义域中存在逻辑依赖关系,则这些依赖关系在笛卡尔积中可能会丢失。决策表通过使用"不可能行动"概念来表示条件的不可能组合,使我们能够强调这种依赖关系。
7.3.1第一次尝试
M1 = {每月有30天的月份:4、6、9、11}
M2 = {每月有31天的月份:1、3、5、7、8、10、12},12月还可以单独的分出来
M3 = {每月有28天的月份:2}
D1={日期:1≤日期≤28}
D2={日期:日期=29}
D3={日期:日期=30}
D4={日期:日期=31}
Y1={年:年是闰年}
Y2={年:年是平年}
决策表如下:
第一次尝试(512条规则)
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
C1:月份在M1中? | T | |||||||||
C2:月份在M2中? | T | |||||||||
C3:月份在M3中 | T | |||||||||
C4:日期在D1中? | ||||||||||
C5:月份在D2中? | ||||||||||
C6:日期在D3中 | ||||||||||
C7:日期在D4中 | ||||||||||
C8:年在Y1中 | ||||||||||
C9:年在Y2中 | ||||||||||
A1:不可能 | ||||||||||
A2:NextDate |
该决策表一共有512条规则,其中很多是不可能的。行动可以进一步细化
A1:月份中的天数太多
A2:不能出现在非闰年中
A3:计算NextDate
7.3.2第二次尝试
等价类集合划分如下:
M2 = {每月有31天的月份:1、3、5、7、8、10、12}
M3 = {每月有28天的月份:2}
D1={日期:1≤日期≤28}
D2={日期:日期=29}
D3={日期:日期=30}
D4={日期:日期=31}
Y1={年:年是一般闰年}
Y2={年:年是一般平年}
Y3={年:年=2000}
Y4={年:年=1900}
年增加了2个关于闰年的特殊年份,2000年和1900年,都是100的倍数,1个是闰年,一个不是。
查看程序源码如下:
int main(void)
{
int tomorrowDay, tomorrowMonth, tomorrowYear;
int day, month, year;
int c1, c2, c3;
do
{
printf("Enter today's date in the form MM DD YYYY");
scanf("%d %d %d", &month, &day, &year);
c1 = (1 <= day) && (day <= 31);
c2 = (1 <= month) && (month <= 12);
c3 = (1812 <= year) && (year <= 2012);
if (!c1)
{
printf("Value of day not in range 1..31\n");
}
if (!c2)
{
printf("value of month not in range 1..12\n");
}
if (!c3)
{
printf("value of year not in range 1812..2012\n");
}
} while (!c1 || !c2 || !c3 );
tomorrowMonth = month;
tomorrowYear = year;
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
if (day < 31)
{
tomorrowDay = day + 1;
}
else
{
tomorrowDay = 1;
tomorrowMonth = month + 1;
}
break;
case 4:
case 6:
case 9:
case 11:
if (day < 30)
{
tomorrowDay = day + 1;
}
else
{
tomorrowDay = 1;
tomorrowMonth = month + 1;
}
break;
case 12:
if (day < 31)
{
tomorrowDay = day + 1;
}
else
{
tomorrowDay = 1;
tomorrowMonth = 1;
if (year == 2012)
{
printf("2012 is over.\n");
return 0;
}
else
{
tomorrowYear = year + 1;
}
}
break;
case 2:
if (day < 28)
{
tomorrowDay = day + 1;
}
else if(day == 28)
{
if(leapYear(year))
{
tomorrowDay = 29;
}
else
{
tomorrowDay = 1;
tomorrowMonth = 3;
}
}
else if (day == 29)
{
tomorrowDay = 1;
tomorrowMonth = 3;
}
else
{
printf("Cannot hanve Feb. %d.\n", day);
return 0;
}
break;
default:
break;
}
printf("Tomorrow date is %d %d %d\n", tomorrowMonth, tomorrowDay, tomorrowYear);
}
研究函数的实现,能够使用的操作有5种:日期增1、日期复位、月份增1、月份复位、年增1,确定行动如下:
A1 = {不可能}
A2 = {日期增1}
A3 = {日期复位}
A4 = {月份增1}
A5 = {月份复位}
A6 = {年份增1}
决策表如下:
桩 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
C1:月份在? | M1 | M1 | M1 | M1 | M2 | M2 | M2 | M2 | M3 | M3 |
C2:日期在? | D1 | D2 | D3 | D4 | D1 | D2 | D3 | D4 | D1 | D1 |
C3:年在? | — | — | — | — | — | — | — | — | Y1 | Y2 |
规则条数 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 1 | 1 |
A1:不可能 | × | |||||||||
A2:日期增1 | × | × | × | × | × | × | ? | |||
A3:日期复位 | × | × | ? | |||||||
A4:月份增1 | × | ? | ? | |||||||
A5:月份复位 | ? | |||||||||
A6:年增1 | ? | |||||||||
桩 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ||
C1:月份在? | M3 | M3 | M3 | M3 | M3 | M3 | M3 | M3 | ||
C2:日期在? | D1 | D1 | D2 | D2 | D2 | D2 | D3 | D4 | ||
C3:年在? | Y3 | Y4 | Y1 | Y2 | Y3 | Y4 | — | — | ||
规则条数 | 1 | 1 | 1 | 1 | 1 | 1 | 4 | 4 | ||
A1:不可能 | × | × | × | × | ||||||
A2:日期增1 | × | ? | ||||||||
A3:日期复位 | ? | × | × | |||||||
A4:月份增1 | ? | × | × | |||||||
A5:月份复位 | ||||||||||
A6:年增1 |
7.3.3 第三次尝试
对12月份进行单独处理,从原来的集合中独立出来,日期方面,将28日从原来的集合中独立出来。
等价类:
M1 = {每月有30天的月份:4、6、9、11}
M2 = {每月有31天的中间月份:1、3、5、7、8、10}
M4 = {每月有31天的最后月份:12}
D1={日期:1≤日期≤27}
D2={日期:日期=28}
D4={日期:日期=30}
D5={日期:日期=31}
Y1={年:年是一般闰年}
Y2={年:年是一般平年}
Y3={年:年=2000}
Y4={年:年=1900}
第三次尝试(80条规则)
桩 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
C1:月份在? | M1 | M1 | M1 | M1 | M2 | M2 | M2 | M2 | ||
C2:日期在? | D1 | D2 | D3 | D4 | D5 | D1 | D2 | D3 | D4 | D5 |
C3:年在? | — | — | — | — | — | — | — | — | — | — |
规则条数 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 | 4 |
A1:不可能 | × | |||||||||
A2:日期增1 | × | × | × | × | × | × | ||||
A3:日期复位 | × | × | ||||||||
A4:月份增1 | × | × | ||||||||
A5:月份复位 | ||||||||||
A6:年增1 | ||||||||||
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | |
C1:月份在? | M3 | M3 | M3 | M3 | M3 | M3 | M3 | M3 | M3 | M3 |
C2:日期在? | D1 | D2 | D2 | D2 | D2 | D3 | D3 | D3 | D3 | D4 |
C3:年在? | — | Y1 | Y2 | Y3 | Y4 | Y1 | Y2 | Y3 | Y4 | — |
规则条数 | 4 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 4 |
A1:不可能 | × | × | × | |||||||
A2:日期增1 | × | × | × | |||||||
A3:日期复位 | × | × | × | × | ||||||
A4:月份增1 | × | × | × | × | ||||||
A5:月份复位 | ||||||||||
A6:年增1 | ||||||||||
桩 | 21 | 22 | 23 | 24 | 25 | 26 | ||||
C1:月份在? | M3 | M4 | M4 | M4 | M4 | |||||
C2:日期在? | D5 | D1 | D2 | D3 | D4 | D5 | ||||
C3:年在? | — | — | — | — | — | — | ||||
规则条数 | 4 | 4 | 4 | 4 | 4 | 4 | ||||
A1:不可能 | × | |||||||||
A2:日期增1 | × | × | × | × | ||||||
A3:日期复位 | × | |||||||||
A4:月份增1 | ||||||||||
A5:月份复位 | × | |||||||||
A6:年增1 | × |
注:标黄部分的部分表示可以合并
生成测试用例:
用例ID | 月份 | 日期 | 年 | 预期输出 |
DT1-3 | 4 | 15 | 2001 | 2001年4月16日 |
DT4 | 4 | 30 | 2001 | 2001年5月1日 |
DT5 | 4 | 31 | 2001 | 不可能 |
DT6-9 | 1 | 15 | 2001 | 2001年1月16日 |
DT10 | 1 | 31 | 2001 | 2001年2月1日 |
DT11 | 2 | 15 | 2001 | 2001年2月16日 |
2 | 28 | 1996 | 1996年2月29日 | |
DT13 | 2 | 28 | 1997 | 1997年3月1日 |
DT14 | 2 | 28 | 2000 | 2000年2月29日 |
DT15 | 2 | 28 | 1900 | 1900年3月1日 |
2 | 29 | 1996 | 1996年3月1日 | |
DT17 | 2 | 29 | 1997 | 不可能 |
DT18 | 2 | 29 | 2000 | 2000年3月1日 |
2 | 29 | 1900 | 不可能 | |
DT20 | 2 | 30 | 2000 | 不可能 |
DT21 | 2 | 31 | 2000 | 不可能 |
DT22-25 | 12 | 30 | 2001 | 2001年12月31日 |
DT26 | 12 | 31 | 2001 | 2002年1月1日 |
7.4 佣金问题的测试用例
决策表分析不太适合佣金问题,这并不奇怪,因为在佣金问题中只有很少的决策逻辑,由于等价类中的变量是真正独立的,在条件对应等价类的决策表中没有不可能规则。因此,得到的测试用例与等价类测试用例一致。
7.5 指导方针与观察
与其他测试技术一样,基于决策表的测试对于某些应用程序(例如NextDate问题)很有效,但对另一些应用程序(例如佣金问题)就不值得费这么大的事,毫不奇怪,基于决策表所适用的情况都是要发生重大决策(例如三角形问题),以及在输入变量之间存在重要的逻辑关系的情况(例如NextDate问题)。
1、决策表技术适用于具有以下特征的应用程序:
If-then-else逻辑很突出
输入变量之间存在逻辑关系
涉及输入变量子集计算
输入与输出之间存在因果关系
很高的圈(McCabe)复杂度