参考答案
我们无法保证答案的正确性,如果你对参考答案有任何疑问,请联系助教!
黑盒测试
题目 I:百分制成绩转换
等价类划分
期望返回只是方便阅读,不要求在实际作答中写出
| 等价类 | 输入范围 | 期望返回 |
|---|---|---|
| 无效低分 | grade < 0 | E |
| F 等级 | 0 <= grade < 60 | F |
| D 等级 | 60 <= grade < 65 | D |
| C 等级 | 65 <= grade < 75 | C |
| B 等级 | 75 <= grade < 85 | B |
| A 等级 | 85 <= grade <= 100 | A |
| 无效高分 | grade > 100 | E |
| 非普通浮点值 | 如 NaN | E |
边界值分析
关键边界为 0、60、65、75、85、100。每个边界都应测试边界点本身,以及边界点左右两侧的值。样例中用 0.1 的偏移表示“略小于/略大于”。
测试样例
期望返回只是方便阅读,不要求在实际作答中写出 覆盖点只是为了方便阅读,不要求在实际作答中写出。
输入 grade | 期望返回 | 覆盖点 |
|---|---|---|
-0.1 | E | 无效低分 |
0 | F | 下边界有效 |
0.1 | F | F 等级内部 |
59.9 | F | 60 左侧 |
60 | D | D 下边界 |
60.1 | D | 60 右侧 |
64.9 | D | 65 左侧 |
65 | C | C 下边界 |
65.1 | C | 65 右侧 |
74.9 | C | 75 左侧 |
75 | B | B 下边界 |
75.1 | B | 75 右侧 |
84.9 | B | 85 左侧 |
85 | A | A 下边界 |
85.1 | A | 85 右侧 |
99.9 | A | 100 左侧 |
100 | A | 上边界有效 |
100.1 | E | 无效高分 |
NaN | E | 非普通浮点值 |
题目 II:容器去重并排序
这道题目因为没有提具体的要求,比较开放,意思对即可。 答案仅供参考,并没有涵盖所有可能的作答角度。
等价类划分
对 container 的等价类:
| 输入条件 | 有效等价类 | 无效等价类 |
|---|---|---|
| 容器类型 | 顺序容器 | 非顺序容器 |
| 容器规模 | 空容器,单元素容器,多元素容器 | - |
| 是否有元素重复 | 无,有部分元素重复,所有元素都重复 | - |
| 容器初始是否已经有序 | 是、否 | - |
| 元素类型 | 支持 < 运算符的元素,不支持的元素 | - |
| 元素值 | 因元素类型而异 |
对 cmp 的等价类:
| 输入条件 | 有效等价类 | 无效等价类 |
|---|---|---|
| 是否缺省 | 是、否 | - |
| 是否满足弱序 | 严格弱序,弱序 | 不满足弱序 |
| 格式是否正确 | 是 | 否 |
格式正确指比较函数类型应为 bool(const Elem&, const Elem&) 或能与其进行隐式转换的类型。
边界值分析
对 container 的等价类:
| 输入条件 | 边界值 |
|---|---|
| 容器类型 | 无 |
| 容器规模 | 容器中有0,1,2个元素 |
| 是否有元素重复 | 容器中有0,1,2种重复元素,容器中除了0,1个元素外都是重复元素 |
| 容器初始是否已经有序 | 除0,1个位置外都有序,除0,1个位置外都逆序 |
| 元素类型 | 无 |
| 元素值 | 因元素类型而异 |
cmp的等价类都没有边界值。
测试样例
期望返回和期望
container,以及说明只是方便阅读,不要求在实际作答中写出 如果你试图去把各种等价类做完全的组合,那你应该深刻地理解到了为什么我们不会去那么做。一般只对那些明显有关的等价类做部分或者完全的组合。
输入 container | 输入 cmp | 期望返回 | 期望 container | 说明 |
|---|---|---|---|---|
set<int>{1, 2, 3, 4} | 缺省 | false | - | 关联容器 |
deque<int>{2, 1, 2} | 缺省 | true | {1, 2} | 另一种有效顺序容器 |
vector<int>{} | 缺省 | true | {} | 空容器 |
vector<int>{1} | 缺省 | true | {1} | 单元素容器 |
vector<int>{1, 2} | 缺省 | true | {1, 2} | 单元素容器边界 |
vector<int>{6, 4, 1, 3, 5, 2} | 缺省 | true | {1, 2, 3, 4, 5, 6} | 多元素容器 |
vector<int>{1, 1, 2, 3, 4, 5} | 缺省 | true | {1, 2, 3, 4, 5} | 单元素重复 |
vector<int>{1, 1, 2, 2, 3, 4, 5} | 缺省 | true | {1, 2, 3, 4, 5} | 单元素重复边界 |
vector<int>{1, 1, 1, 1, 1, 1} | 缺省 | true | {1} | 全部重复 |
vector<int>{1, 1, 1, 3, 1, 1} | 缺省 | true | {1, 3} | 全部重复边界 |
vector<int>{4, 6, 9, 10, 23, 43} | 缺省 | true | {4, 6, 9, 10, 23, 43} | 已经有序 |
vector<int>{4, 6, 10, 9, 23, 43} | 缺省 | true | {4, 6, 9, 10, 23, 43} | 去除一个元素后有序 |
vector<int>{43, 23, 10, 9, 6, 4} | 缺省 | true | {4, 6, 9, 10, 23, 43} | 完全逆序 |
vector<int>{43, 23, 9, 10, 6, 4} | 缺省 | true | {4, 6, 9, 10, 23, 43} | 去除一个元素后完全逆序 |
vector<vector<int>>{{1}, {2}, {3}} | 缺省 | false | - | 不支持 < 的元素 |
vector<vector<int>>{{1}, {2}, {3}} | [](const vector<int>& lhs, const vector<int>& rhs){ return lhs[0] < rhs[0]; } | true | {{1}, {2}, {3}} | 不支持 < 的元素但是传入了正确的比较器 |
vector<int>{43, 23, 9, 10, 6, 4} | [](const int& lhs, const int& rhs){ return lhs <= rhs; } | true | {4, 6, 9, 10, 23, 43} | 传入比较器满足弱序但非严格弱序 |
vector<int>{43, 23, 9, 10, 6, 4} | [](const int& lhs, const int& rhs){ return true; } | 未定义 | 未定义 | 传入比较器不满足弱序 |
vector<int>{43, 23, 9, 10, 6, 4} | [](const string& lhs, const string& rhs){ return lhs[0] < rhs[0]; } | false | - | 传入非法比较器 |
白盒测试
说明:覆盖代码行中的 T/F 表示对应判定行取真/取假;复合条件表中列出的是各原子条件的逻辑取值。
题目 I
待测代码
void exercise_wb_1(int param1, int param2, int param3)
{
int res = -1;
if (param1 > 0 && param2 > 0 && param3 > 0)
{
if (param1 > param2)
{
if (param2 > param3)
{
res = 3;
}
else if (param1 > param3)
{
res = 2;
}
else
{
res = 1;
}
}
else
{
if (param2 > param3)
{
if (param1 > param3)
{
res = 2;
}
else
{
res = 1;
}
}
else
{
res = 0;
}
}
}
cout << res << endl;
}
语句覆盖
期望输出和覆盖代码行只是方便阅读,不要求在实际作答中写出
要覆盖所有赋值语句,需要进入每个内层路径。以下 6 个样例可以覆盖所有行。
| 编号 | (param1, param2, param3) | 期望输出 | 主要覆盖目标代码行 |
|---|---|---|---|
| WB1-L1 | (3, 2, 1) | 3 | L10 |
| WB1-L2 | (3, 1, 2) | 2 | L14 |
| WB1-L3 | (2, 1, 3) | 1 | L18 |
| WB1-L4 | (2, 3, 1) | 2 | L27 |
| WB1-L5 | (1, 3, 2) | 1 | L31 |
| WB1-L6 | (1, 2, 3) | 0 | L36 |
分支覆盖
期望输出和覆盖分支只是方便阅读,不要求在实际作答中写出
在语句覆盖样例基础上增加一个外层条件为假的样例即可覆盖外层 if 的假分支。
| 编号 | (param1, param2, param3) | 期望输出 | 覆盖分支 |
|---|---|---|---|
| WB1-B1 | (3, 2, 1) | 3 | L04(T), L06(T), L08(T) |
| WB1-B2 | (3, 1, 2) | 2 | L04(T), L06(T), L08(F), L12(T) |
| WB1-B3 | (2, 1, 3) | 1 | L04(T), L06(T), L08(F), L12(F) |
| WB1-B4 | (2, 3, 1) | 2 | L04(T), L06(F), L23(T), L25(T) |
| WB1-B5 | (1, 3, 2) | 1 | L04(T), L06(F), L23(T), L25(F) |
| WB1-B6 | (1, 2, 3) | 0 | L04(T), L06(F), L23(F) |
| WB1-B7 | (0, 1, 1) | -1 | L04(F) |
条件组合覆盖
只有L04是复杂条件,其余都是简单条件。简单条件的真值组合就是他自己取真值或者假值。
对于复杂条件,先列出组成它的所有简单条件:
P1 = param1 > 0
P2 = param2 > 0
P3 = param3 > 0
需要覆盖 8 种组合。除此之外,还要覆盖到剩余简单条件的真假值,能进入内层循环的只有一组真值组合,在满足这个真值组合的前提下直接复用之前的样例就行。
| 编号 | (param1, param2, param3) | L04 的 (A, B, C) | 其他覆盖代码行 | 期望输出 |
|---|---|---|---|---|
| WB1-C1 | (3, 2, 1) | (T, T, T) | L06(T), L08(T) | 3 |
| WB1-C2 | (3, 1, 2) | (T, T, T) | L06(T), L08(F), L12(T) | 2 |
| WB1-C3 | (2, 1, 3) | (T, T, T) | L06(T), L08(F), L12(F) | 1 |
| WB1-C4 | (2, 3, 1) | (T, T, T) | L06(F), L23(T), L25(T) | 2 |
| WB1-C5 | (1, 3, 2) | (T, T, T) | L06(F), L23(T), L25(F) | 1 |
| WB1-C6 | (1, 2, 3) | (T, T, T) | L06(F), L23(F) | 0 |
| WB1-C7 | (0, 1, 1) | (F, T, T) | L04(F) | -1 |
| WB1-C8 | (1, 0, 1) | (T, F, T) | L04(F) | -1 |
| WB1-C9 | (1, 1, 0) | (T, T, F) | L04(F) | -1 |
| WB1-C10 | (0, 0, 1) | (F, F, T) | L04(F) | -1 |
| WB1-C11 | (0, 1, 0) | (F, T, F) | L04(F) | -1 |
| WB1-C12 | (1, 0, 0) | (T, F, F) | L04(F) | -1 |
| WB1-C13 | (0, 0, 0) | (F, F, F) | L04(F) | -1 |
题目 II
待测代码
void exercise_wb_2(int param1, int param2)
{
int param3 = param1 + param2;
if (param3 > 0)
{
++param1;
}
if (param3 < 0)
{
++param2;
}
if (param1 > param2 || param1 > param3)
{
if (param1 <= param2 || param1 <= param3)
{
param3 += param1;
}
}
if (param3 != 0)
{
param3 += param2;
}
cout << param3 << endl;
}
语句覆盖
| 编号 | (param1, param2) | 期望输出 | 主要覆盖目标代码行 |
|---|---|---|---|
| WB2-L1 | (1, 1) | 5 | L06, L16, L21 |
| WB2-L2 | (-1, -1) | -3 | L10 |
分支覆盖
| 编号 | (param1, param2) | 期望输出 | 覆盖分支 |
|---|---|---|---|
| WB2-B1 | (1, 1) | 5 | L04(T), L08(F), L12(T), L14(T), L19(T) |
| WB2-B2 | (0, -2) | -3 | L04(F), L08(T), L12(T), L14(F), L19(T) |
| WB2-B3 | (0, 0) | 0 | L04(F), L08(F), L12(F), L19(F) |
条件组合覆盖
第三个判断的外层复合条件记为(这里的 param1、param2、param3 都指执行完前两个 if 后的当前值):
P = param1 > param2
Q = param1 > param3
内层复合条件等价于:
R = param1 <= param2 = !P
S = param1 <= param3 = !Q
因此当 (R, S) = (T, T) 时,外层 (P || Q) 必为假,内层判断不可达。但条件覆盖与条件组合覆盖无需考虑可达性,只要将对应真假值取到即可。下面样例覆盖外层 P/Q 的 4 种组合,并覆盖内层所有的条件组合。
| 编号 | (param1, param2) | 外层 (P, Q) | 内层 (R, S) | 期望输出 |
|---|---|---|---|---|
| WB2-C1 | (0, -2) | L12: (T, T) | L14: (F, F) | -3 |
| WB2-C2 | (1, 1) | L12: (T, F) | L14: (F, T) | 5 |
| WB2-C3 | (0, -1) | L12: (F, T) | L14: (T, F) | -1 |
| WB2-C4 | (0, 0) | L12: (F, F) | L14: (T, T) 不可达,但依然覆盖 | 0 |
本题虽然内层条件
(T, T)取值不可达,但依然达到了100%的条件组合覆盖
题目 III
待测代码
用人脑理解复杂的代码逻辑是很困难的,特别是没有注释的时候!
void exercise_wb_3(vector<int> params)
{
bool flag = true;
int res = 0;
int pos = 0;
int sz = params.size();
while (pos != sz)
{
if (flag)
{
res += params[pos];
}
else
{
res -= params[pos];
}
if (params[pos] > 0)
{
flag = !flag;
}
++pos;
}
if (res > 0)
{
res = res * res;
}
if (res < 0)
{
res = -(res * res);
}
cout << res << endl;
}
语句覆盖
| 编号 | params | 期望输出 | 主要覆盖目标代码行 |
|---|---|---|---|
| WB3-L1 | {2, 1, 0} | 1 | L11, L15, L19, L21, L25 |
| WB3-L2 | {1, 2} | -1 | L29 |
分支覆盖
| 编号 | params | 期望输出 | 覆盖分支 |
|---|---|---|---|
| WB3-B1 | {2, 1, 0} | 1 | L07(T&F), L09(T&F), L17(T&F), L23(T), L27(F) |
| WB3-B2 | {1, 2} | -1 | L23(F), L27(T) |
条件组合覆盖
本题没有复合条件,条件组合覆盖和条件覆盖是等价的,也和分支覆盖是等价的。
| 编号 | params | 期望输出 | 覆盖条件 |
|---|---|---|---|
| WB3-C1 | {2, 1, 0} | 1 | L07(T&F), L09(T&F), L17(T&F), L23(T), L27(F) |
| WB3-C2 | {1, 2} | -1 | L23(F), L27(T) |