2025/04/22
实验项目名称:实验一
一、 实验目的
- 掌握黑盒测试的基础知识;
- 掌握黑盒测试的检查内容及测试目的;
- 掌握黑盒测试的几种基本测试方法:等价类划分方法、边界值分析方法、因果图法和决策表法;
二、 实验内容
1、 设计函数实现输入日期显示星期几,并用等价类及边界值法测试
2、 找零钱最佳组合:假设商店货品价格(R) 都不大于100元(且为整数),若顾客付款(P)在100元内,现有一个程序能在每位顾客付款后给出找零钱的最佳组合(找给顾客货币张数最少)。 假定此商店的货币面值只包括:50元(N50)、10元(N10)、 5元(N5)、1元(N1) 四种。请结合等价类划分法和边界值分析法为上述程序设计 出相应的测试用例。
3、 有一个饮料自动售货机(处理单价为5角钱)的控制处理软件,它的软件规格说明如下:若投入5角钱的硬币,按下“橙汁”或“啤酒”的按钮,则相应的饮料就送出来。若投入1元钱的硬币,同样也是按“橙汁”或“啤酒”的按钮,则自动售货机在送出相应饮料的同时退回5角钱的硬币。
4、 航空服务查询问题:根据航线,仓位,飞行时间查询航空服务。假设一个中国的航空公司规定:① 中国去欧美的航线所有座位都有食物供应,每个座位都可以播放电影。② 中国去非欧美的国外航线都有食物供应,只有商务仓可以播放电影。③ 中国国内的航班的商务仓有食物供应,但是不可以播放电影④ 中国国内航班的经济仓只有当飞行时间大于2小时时才有食物供应,但是不可以播放电影。请用程序实现上述功能,并用决策表法设计测试用例,再执行测试,撰写实验报告。
5、 旅馆住宿系统中,旅馆业主可进行添加房间操作。
– 旅馆业主登录旅馆住宿系统后,可以请求添加房间;
– 待进入“房间管理”对话框,单击“添加”按钮可进行添加房间操作;
– 添加房间时,可以设定房间的房间编号、房间类型、房间描述信息;
– 添加房间信息不能缺失,若某一项未填写,要给出提示信息;
– 房间编号长度不超过5个字符;
– 房间描述长度不超过1000个字符;
– 房间信息不能重复,成功填写后,可进行保存或取消操作,之后返回“房间管理”对话框,结束添加房间流程。
实验步骤:
利用黑盒测试策略编写添加房间功能的测试用例。
三、 实验步骤及结果
实验步骤:
1、
① 设计程序
public class GetWeekday {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个日期(格式为yyyyMMdd):");
String dateStr = scanner.nextLine();
System.out.println(findWeekday(dateStr));
}
public static String findWeekday(String dateStr) {
try {
int year = Integer.parseInt(dateStr.substring(0, 4));
int month = Integer.parseInt(dateStr.substring(4, 6));
int day = Integer.parseInt(dateStr.substring(6));
//不会判断二月是否有29、30、31要手动重新判断
if (month == 2) {
boolean isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
if (isLeapYear && day > 29) {
return "输入日期格式不正确,请输入正确的日期格式。";
} else if (!isLeapYear && day > 28) {
return "输入日期格式不正确,请输入正确的日期格式。";
}
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate date = LocalDate.parse(dateStr, formatter);
return date.getDayOfWeek().toString();
} catch (DateTimeParseException e) {
return "输入日期格式不正确,请输入正确的日期格式。";
}
}
}
② 划分等价类,得到等价类表。等价类表格式如下:
输入条件 |
有效等价类 |
唯一标识 |
无效等价类 |
唯一标识 |
年 |
1900到2050内的闰年 |
(1) |
Year<1900 |
(10) |
|
1900到2050内的平年 |
(2) |
Year>2050 |
(11) |
|
|
|
非数字 |
(12) |
月 |
1、3、5、7、8、10、12 |
(3) |
Month<1 |
(13) |
|
4、6、9、11 |
(4) |
Month>12 |
(14) |
|
2 |
(5) |
非数字 |
(15) |
日 |
1-28 |
(6) |
Day<1 |
(16) |
|
29 |
(7) |
Month=1,3,5,7,8,10,12时,Day>31 |
(17) |
|
30 |
(8) |
Month=4,6,9,11 时,Day>30 |
(18) |
|
31 |
(9) |
Year为平年,Month为2时,Day>28 |
(19) |
|
|
|
Year为闰年,Month为2时,Day>29 |
(20) |
|
|
|
非数字 |
(21) |
③ 运用等价类划分法设计测试用例,得到测试用例表。测试用例表格式如下:
序号 |
输入数据 |
覆盖等价类 |
输出 |
1 |
20000131 |
1、3、9 |
MONDAY |
2 |
20010430 |
2、4、8 |
MONDAY |
3 |
20000229 |
1、5、7 |
THUSDAY |
4 |
20010228 |
2、5、6 |
WEDNESDAY |
5 |
18990101 |
10、3、6 |
输入日期格式不正确,请输入正确的日期格式。 |
6 |
20510101 |
11、3、6 |
输入日期格式不正确,请输入正确的日期格式。 |
7 |
abcd0101 |
12、3、6 |
输入日期格式不正确,请输入正确的日期格式。 |
8 |
20000001 |
1、13、6 |
输入日期格式不正确,请输入正确的日期格式。 |
9 |
20001301 |
1、14、6 |
输入日期格式不正确,请输入正确的日期格式。 |
10 |
2000ab01 |
1、15、6 |
输入日期格式不正确,请输入正确的日期格式。 |
11 |
20000100 |
1、3、16 |
输入日期格式不正确,请输入正确的日期格式。 |
12 |
20000132 |
1、3、17 |
输入日期格式不正确,请输入正确的日期格式。 |
13 |
20000431 |
1、3、18 |
输入日期格式不正确,请输入正确的日期格式。 |
14 |
20010229 |
1、3、19 |
输入日期格式不正确,请输入正确的日期格式。 |
15 |
20000230 |
1、3、20 |
输入日期格式不正确,请输入正确的日期格式。 |
16 |
200001ab |
1、3、21 |
输入日期格式不正确,请输入正确的日期格式。 |
④ 运用边界值法设计测试用例。
序号 |
输入数据 |
覆盖等价类 |
输出 |
1 |
19000101 |
1、3、6 |
MONDAY |
2 |
19000228 |
2、5、6 |
WEDNESDAY |
3 |
20000229 |
1、5、7 |
TUESDAY |
4 |
20250430 |
2、4、8 |
WEDNESDAY |
5 |
20250131 |
2、3、9 |
FRIDAY |
6 |
18990101 |
10、3、6 |
输入日期格式不正确,请输入正确的日期格式。 |
7 |
20510101 |
11、3、6 |
输入日期格式不正确,请输入正确的日期格式。 |
8 |
abcd0101 |
12、3、6 |
输入日期格式不正确,请输入正确的日期格式。 |
9 |
20250001 |
13、2、6 |
输入日期格式不正确,请输入正确的日期格式。 |
10 |
20251301 |
14、2、6 |
输入日期格式不正确,请输入正确的日期格式。 |
11 |
2025ab01 |
15、2、6 |
输入日期格式不正确,请输入正确的日期格式。 |
12 |
20250100 |
16、2、3 |
输入日期格式不正确,请输入正确的日期格式。 |
13 |
20250132 |
17、2、3 |
输入日期格式不正确,请输入正确的日期格式。 |
14 |
20250431 |
18、2、4 |
输入日期格式不正确,请输入正确的日期格式。 |
15 |
19010229 |
19、2、5 |
输入日期格式不正确,请输入正确的日期格式。 |
16 |
20000230 |
20、1、5 |
输入日期格式不正确,请输入正确的日期格式。 |
17 |
202501ab |
21、2、3 |
输入日期格式不正确,请输入正确的日期格式。 |
2、实验步骤:
①等价类表:
输入条件 |
有效等价类 |
唯一标识 |
无效等价类 |
唯一标识 |
货品价格 |
0<R<100 |
(1) |
R=>100 |
(3) |
|
R为整数 |
(2) |
R<=0 |
(4) |
|
|
|
R不为整数 |
(5) |
付款 |
0<P<100 |
(6) |
P>=100 |
(9) |
|
P为整数 |
(7) |
P<=0 |
(10) |
|
P>=R |
(8) |
P不为整数 |
(11) |
|
|
|
R>P |
(12) |
②运用等价类划分法设计测试用例
序号 |
输入数据 |
覆盖等价类 |
输出 |
1 |
(50, 60) |
(1)(2)(6)(7)(8) |
找零组合如下: |
2 |
(100, 99) |
(3)(2)(6)(7)(8) |
输入有误,请检查货品价格和付款金额。 |
3 |
(-10, 50) |
(4)(2)(6)(7)(8) |
输入有误,请检查货品价格和付款金额。 |
4 |
(50.5, 60) |
(5)(1)(6)(7)(8) |
输入有误,请检查货品价格和付款金额。 |
5 |
(50, 100) |
(9)(1)(2)(7)(8) |
输入有误,请检查货品价格和付款金额。 |
6 |
(50, -10) |
(10)(1)(2)(7)(8) |
输入有误,请检查货品价格和付款金额。 |
7 |
(50, 50.5) |
(11)(1)(2)(6)(8) |
输入有误,请检查货品价格和付款金额。 |
8 |
(50, 49) |
(12)(1)(2)(6)(7) |
输入有误,请检查货品价格和付款金额。 |
③运用边界值法设计测试用例。
序号 |
输入数据 |
覆盖等价类 |
输出 |
1 |
(1, 1) |
R 最小值,P = R |
找零组合如下: 50 元: 0 张 10 元: 0 张 5 元: 0 张 1 元: 0 张 |
2 |
(1, 2) |
R 最小值,P 稍大于 R |
找零组合如下: 50 元: 0 张 10 元: 0 张 5 元: 0 张 1 元: 1 张 |
3 |
(99, 99) |
R 最大值,P = R |
找零组合如下: 50 元: 0 张 10 元: 0 张 5 元: 0 张 1 元: 0 张 |
4 |
(99, 100) |
R 最大值,P 刚好能覆盖找零 |
找零组合如下: 50 元: 0 张 10 元: 0 张 5 元: 0 张 1 元: 1 张 |
5 |
(0, 1) |
R 边界外最小值,无效情况 |
输入有误,请检查货品价格和付款金额。 |
6 |
(100, 101) |
R 边界外最大值,P 边界外值,无效情况 |
输入有误,请检查货品价格和付款金额。 |
7 |
(1, 0) |
P 边界外最小值,无效情况 |
输入有误,请检查货品价格和付款金额。 |
8 |
(1, 100) |
P 边界值,无效情况 |
输入有误,请检查货品价格和付款金额。 |
④设计程序
public class Change {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("请输入货品价格 (R <= 100): ");
int R = scanner.nextInt();
System.out.print("请输入顾客付款金额 (P <= 100): ");
int P = scanner.nextInt();
if (R >= 100 || P >= 100 || P < R || R <= 0 || P <= 0) {
System.out.println("输入有误,请检查货品价格和付款金额。");
} else {
int change = P - R;
int N50 = change / 50;
change %= 50;
int N10 = change / 10;
change %= 10;
int N5 = change / 5;
change %= 5;
int N1 = change;
System.out.println("找零组合如下:");
System.out.println("50 元: " + N50 + " 张");
System.out.println("10 元: " + N10 + " 张");
System.out.println("5 元: " + N5 + " 张");
System.out.println("1 元: " + N1 + " 张");
}
}catch (Exception e)
{
System.out.println("输入有误,请检查货品价格和付款金额。");
}
}
}
3、实验步骤:
①编写程序
public class VendingMachineGUI extends JFrame {
private ButtonGroup coinGroup;
private ButtonGroup drinkGroup;
private JButton purchaseButton;
private JLabel resultLabel;
public VendingMachineGUI() {
setTitle("饮料自动售货机");
setSize(350, 350);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
// 主面板,使用 GridLayout 布局
JPanel mainPanel = new JPanel(new GridLayout(4, 1, 10, 10));
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
// 投币选择部分
JLabel coinLabel = new JLabel("请投币:");
coinGroup = new ButtonGroup();
JRadioButton fiftyCent = new JRadioButton("五角");
JRadioButton oneDollar = new JRadioButton("一元");
coinGroup.add(fiftyCent);
coinGroup.add(oneDollar);
JPanel coinPanel = new JPanel();
coinPanel.add(coinLabel);
coinPanel.add(fiftyCent);
coinPanel.add(oneDollar);
mainPanel.add(coinPanel);
// 饮料选择部分
JLabel drinkLabel = new JLabel("请选择饮料:");
drinkGroup = new ButtonGroup();
JRadioButton orangeJuice = new JRadioButton("橙汁");
JRadioButton beer = new JRadioButton("啤酒");
drinkGroup.add(orangeJuice);
drinkGroup.add(beer);
JPanel drinkPanel = new JPanel();
drinkPanel.add(drinkLabel);
drinkPanel.add(orangeJuice);
drinkPanel.add(beer);
mainPanel.add(drinkPanel);
// 购买按钮面板
JPanel buttonPanel = new JPanel();
purchaseButton = new JButton("购买");
buttonPanel.add(purchaseButton);
mainPanel.add(buttonPanel);
// 结果显示标签面板
JPanel resultPanel = new JPanel();
resultLabel = new JLabel("");
resultPanel.add(resultLabel);
mainPanel.add(resultPanel);
add(mainPanel, BorderLayout.CENTER);
// 为购买按钮添加点击事件监听器
purchaseButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String coin = "";
if (fiftyCent.isSelected()) {
coin = "五角";
} else if (oneDollar.isSelected()) {
coin = "一元";
}
String drink = "";
if (orangeJuice.isSelected()) {
drink = "橙汁";
} else if (beer.isSelected()) {
drink = "啤酒";
}
String result;
if (coin.equals("五角")) {
result = "已为您送出 " + drink + "。";
} else if (coin.equals("一元")) {
result = "已为您送出 " + drink + ",并退回五角硬币。";
} else {
result = "请选择投币金额。";
}
resultLabel.setText(result);
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
VendingMachineGUI vendingMachine = new VendingMachineGUI();
vendingMachine.setVisible(true);
}
});
}
}
②分析原因与结果
原因(输入条件)
C1:投入 5 角硬币
C2:投入 1 元硬币
C3:按下 “橙汁” 按钮
C4:按下 “啤酒” 按钮
结果(输出动作)
E1:送出橙汁饮料
E2:送出啤酒饮料
E3:退回 5 角硬币
④ 画出因果图
④转化为决策表
规则编号 |
C1 投入 5 角硬币 |
C2 投入 1 元硬币 |
C3 按下 “橙汁” 按钮 |
C4 按下 “啤酒” 按钮 |
E1 送出橙汁饮料 |
E2 送出啤酒饮料 |
E3 退回 5 角硬币 |
1 |
1 |
0 |
1 |
0 |
1 |
0 |
0 |
2 |
1 |
0 |
0 |
1 |
0 |
1 |
0 |
3 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
4 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
5 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
6 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
7 |
1 |
1 |
- |
- |
- |
- |
- |
8 |
- |
- |
0 |
0 |
0 |
0 |
0 |
⑤根据决策表设计测试用例,得到测试用例表
用例编号 |
输入(投入硬币、按下按钮) |
预期输出(送出饮料、退回硬币) |
1 |
投入 5 角,按下 “橙汁” 按钮 |
送出橙汁饮料,无退回硬币 |
2 |
投入 5 角,按下 “啤酒” 按钮 |
送出啤酒饮料,无退回硬币 |
3 |
投入 1 元,按下 “橙汁” 按钮 |
送出橙汁饮料,退回 5 角硬币 |
4 |
投入 1 元,按下 “啤酒” 按钮 |
送出啤酒饮料,退回 5 角硬币 |
5 |
不投入硬币,按下 “橙汁” 按钮 |
无饮料送出,无退回硬币 |
6 |
不投入硬币,按下 “啤酒” 按钮 |
无饮料送出,无退回硬币 |
7 |
投入 5 角和 1 元(异常) |
提示错误信息 |
8 |
投入硬币,不按任何按钮 |
无饮料送出,无退回硬币 |
4、实验步骤:
① 编写程序
public class AirlineServiceQuery extends JFrame {
private JRadioButton routeToEuropeAmerica;
private JRadioButton routeToOtherForeign;
private JRadioButton routeDomestic;
private JRadioButton classBusiness;
private JRadioButton classEconomy;
private JRadioButton flightTimeOver2Hours;
private JRadioButton flightTimeUnder2Hours;
private JButton queryButton;
private JTextArea resultArea;
public AirlineServiceQuery() {
setTitle("航空公司服务查询");
setSize(400, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
// 航线选择
ButtonGroup routeGroup = new ButtonGroup();
routeToEuropeAmerica = new JRadioButton("中国去欧美的航线");
routeToOtherForeign = new JRadioButton("中国去非欧美的国外航线");
routeDomestic = new JRadioButton("中国国内的航班");
routeGroup.add(routeToEuropeAmerica);
routeGroup.add(routeToOtherForeign);
routeGroup.add(routeDomestic);
// 仓位选择
ButtonGroup classGroup = new ButtonGroup();
classBusiness = new JRadioButton("商务仓");
classEconomy = new JRadioButton("经济仓");
classGroup.add(classBusiness);
classGroup.add(classEconomy);
// 飞行时间选择
ButtonGroup flightTimeGroup = new ButtonGroup();
flightTimeOver2Hours = new JRadioButton("飞行时间大于2小时");
flightTimeUnder2Hours = new JRadioButton("飞行时间小于等于2小时");
flightTimeGroup.add(flightTimeOver2Hours);
flightTimeGroup.add(flightTimeUnder2Hours);
// 查询按钮
queryButton = new JButton("查询");
queryButton.addActionListener(new QueryButtonListener());
// 结果显示区域
resultArea = new JTextArea(10, 30);
resultArea.setEditable(false);
// 添加组件到窗口
add(routeToEuropeAmerica);
add(routeToOtherForeign);
add(routeDomestic);
add(classBusiness);
add(classEconomy);
add(flightTimeOver2Hours);
add(flightTimeUnder2Hours);
add(queryButton);
add(new JScrollPane(resultArea));
}
private class QueryButtonListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
boolean hasFood = false;
boolean canPlayMovie = false;
if (routeToEuropeAmerica.isSelected()) {
hasFood = true;
canPlayMovie = true;
} else if (routeToOtherForeign.isSelected()) {
hasFood = true;
canPlayMovie = classBusiness.isSelected();
} else if (routeDomestic.isSelected()) {
if (classBusiness.isSelected()) {
hasFood = true;
canPlayMovie = false;
} else if (classEconomy.isSelected()) {
hasFood = flightTimeOver2Hours.isSelected();
canPlayMovie = false;
}
}
String result = "食物供应: " + (hasFood ? "有" : "无") + "\n";
result += "电影播放: " + (canPlayMovie ? "可以" : "不可以");
resultArea.setText(result);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
AirlineServiceQuery query = new AirlineServiceQuery();
query.setVisible(true);
});
}
}
② 构造决策表
条件:
C1:航线是否为中国去欧美的航线
C2:航线是否为中国去非欧美的国外航线
C3:航线是否为中国国内航班
C4:仓位是否为商务仓
C5:飞行时间是否大于 2 小时
动作:
A1:有食物供应
A2:可以播放电影
规则编号 |
C1 |
C2 |
C3 |
C4 |
C5 |
A1 |
A2 |
1 |
Y |
N |
N |
- |
- |
Y |
Y |
2 |
N |
Y |
N |
Y |
- |
Y |
Y |
3 |
N |
Y |
N |
N |
- |
Y |
N |
4 |
N |
N |
Y |
Y |
- |
Y |
N |
5 |
N |
N |
Y |
N |
Y |
Y |
N |
6 |
N |
N |
Y |
N |
N |
N |
N |
③ 根据决策表设计测试用例,得到测试用例表
用例编号 |
航线 |
仓位 |
飞行时间 |
预期结果(食物供应) |
预期结果(电影播放) |
1 |
中国去欧美的航线 |
任意 |
任意 |
有 |
可以 |
2 |
中国去非欧美的国外航线 |
商务仓 |
任意 |
有 |
可以 |
3 |
中国去非欧美的国外航线 |
经济仓 |
任意 |
有 |
不可以 |
4 |
中国国内航班 |
商务仓 |
任意 |
有 |
不可以 |
5 |
中国国内航班 |
经济仓 |
大于 2 小时 |
有 |
不可以 |
6 |
中国国内航班 |
经济仓 |
小于等于 2 小时 |
无 |
不可以 |
5、
等价类划分
输入条件 |
有效等价类 |
无效等价类 |
房间编号 |
|
|
房间编号必填 |
填写合法编号(如:A001) |
不填写(空) |
编号长度≤5 字符 |
1-5 个字符 |
6 个及以上字符 |
编号唯一性 |
未注册过的编号 |
已注册过的编号 |
字符类型 |
纯数字 / 字母 |
含特殊符号 |
房间类型 |
|
|
房间类型必填 |
填写合法类型 |
不填写 |
类型枚举 |
系统允许的类型 |
非法类型 |
房间描述 |
|
|
房间描述必填 |
填写合法描述 |
不填写 |
描述长度≤1000 字符 |
1-1000 个字符 |
1001 个及以上字符 |
边界值分析
1房间编号长度边界
边界点:5(有效最大值)、6(无效最小值)
测试用例:
有效边界:编号为ABCDE(5 个字符),验证是否允许保存。
无效边界:编号为ABCDEF(6 个字符),验证是否提示 “长度超过限制”。
2房间描述长度边界
边界点:1000(有效最大值)、1001(无效最小值)
测试用例:
有效边界:描述为重复a字符 1000 次,验证是否允许保存。
无效边界:描述为重复a字符 1001 次,验证是否提示 “长度超过限制”。
测试用例设计(结合等价类与边界值)
用例编号 |
测试场景 |
输入数据 |
预期结果 |
1 |
有效编号(5 字符)、有效类型、有效描述 |
编号:A0001(5 字符) 类型:单人间 |
保存成功 |
2 |
编号为空(必填项) |
编号:空 类型:单人间 描述:正常描述 |
提示 “房间编号不能为空”。 |
3 |
编号 6 字符(超长) |
编号:A00001(6 字符) 类型:单人间 描述:正常描述 |
提示 “房间编号长度不能超过 5 个字符”。 |
4 |
重复编号(唯一性) |
编号:A001(已存在) 类型:单人间 描述:新描述 |
提示 “房间信息已存在,不能重复添加”。 |
5 |
描述 1000 字符(边界值) |
描述:重复a字符 1000 次 |
保存成功。 |
6 |
描述 1001 字符(超边界) |
描述:重复a字符 1001 次 |
提示 “房间描述长度不能超过 1000 个字符”。 |
7 |
类型为空(必填项) |
编号:A002 类型:空 描述:正常描述 |
提示 “房间类型不能为空”。 |
8 |
编号含特殊符号(非法字符) |
编号:A#001 类型:单人间 描述:含符号 |
若系统限制纯数字 / 字母,提示错误;若允许符号则保存成功(需根据需求调整)。 |
9 |
类型为空(必填项) |
编号:A002 类型:单人间 描述:空 |
提示 “房间描述不能为空”。 |