软件测试学习——TDD流程实验报告

软件测试课程作业四

实验内容

  • 依据 TDD 编程流程,编制函数,实现两数相加求和。

实验过程

TDD 需求阶段

考虑到浮点数可表达的范围太大不好体现 TDD 设计流程的思想,本实验目标为实现用户控制两整数相加并返回结果。

  • 输入阶段:

    用户可手动输入数值或选择退出程序。

  • 检测阶段:对用户输入的内容进行检测

    合法输入应为int类型范围内整数,对于不合法的输入提示错误并重新输入,输入为合法范围内但输出数值溢出的情况,会由计算阶段程序在内部进行适当格式转化,对用户透明。

  • 计算阶段:计算两数之和

    对合法的输入内容,执行 ADD 求和,并返回结果。

  • 输出阶段:

    将得到的结果反馈给用户。

TDD 设计阶段

基于需求分析的思路,设计方案求精,构建测试用例,用文字描述程序过程。(单元测试 Unit Test 客观上被节省掉)。

  • 构建测试用例

    由于开发过程是,需求-测例-代码,看不到程序结构,这里主要采用黑盒测试的测例构造思路。

    测例编号 输入 预计输出 实际输出 备注
    1 a=2147483646 b=1 2147483647 4Bytes int 类型边界测试值。
    2 a=2147483647 b=1 2147483647 输出值不再能用 4Bytes 表示,转化测试
    3 a=2147483647 b=2147483647 4294967294 双边界值测试
    4 a=-2147483647 b=-1 -2147483648 负值 4Bytes int 类型边界测试界
    5 a=-2147483648 b=-1 -2147483649 负值转化测试
    6 a=-2147483648 b=-2147483648 -4294967296 负值双边界值测试
    7 a=0.005 b = asdfab 2 Error 不合法的非整数输入测试
    8 a=2147483648 b=-2147483649 2 Error 正负双越界测试

TDD 构造阶段

在我的构造阶段,我写了一个带格式转化的程序(验证程序)和一个不带格式转化的程序(对照程序)。

  • 带格式转化程序(验证程序)
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
127
128
129
130
131
132
/*
** 程序名:testAdd
** 程序功能:从input输入文件中逐行输入两个整数值
** 在输入合法的情况下(范围4字节有符号int类型)打印运算成功并输出和值。
** 否则打印错误信息
** 作者:Edwardzcn
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<string, string> pss;
#define rep(i, a, n) for (int i = a; i < n; ++i)
#define per(i, a, n) for (int i = n - 1; i >= a; --i)

#define DEBUG_INPUT DEBUG_INPUT

/*
** 函数名:isNum
** 函数功能:用来检测是否为合法输入数
** 参数名称与类型:
** string s
** 返回值:
** bool
*/
bool isNum(string s)
{
bool flag = false;
if (s == "")
return false;
rep(i, 0, s.size())
{
if (i == 0 && s[i] == '-')
continue;
if (s[i] < '0' || s[i] > '9')
return false;
}
return true;
}

/*
** 函数名:getAdd
** 函数功能:根据两输入值获取商值
** 参数名称与类型:
** int a
** int b
** 返回值类型:
** long long
*/
ll getAdd(int a, int b)
{
return ll(a) + ll(b);
}

/*
** 函数名:main
** 函数功能:主函数,入口
** 参数名称与类型:
** int argc
** char* argv[]
** 返回值类型:
** int
*/
int main(int argc, char *argv[])
{
cout << ("======= Start Program: testAdd ================") << endl;

#ifdef DEBUG_INPUT
freopen("input", "r", stdin);
// freopen("output", "w", stdout);
#endif // DEBUG_INPUT
string a, b;
int parseNum;
while (cin >> a >> b)
{
bool a_type = isNum(a);
bool b_type = isNum(b);
int a_int, b_int;
int flag = true;
// 判断非数字的情况
if (!a_type)
{
flag = false;
std::cerr << "Error: a=" << a << " is not a num." << endl;
}
if (!b_type)
{
flag = false;
std::cerr << "Error: b=" << b << " is not a num." << endl;
}
if (!flag)
{
cout << "Try Again." << endl;
continue;
}

try
{
a_int = stoi(a, string::size_type());
}
catch (const std::out_of_range &oor)
{
std::cerr << "Error: Value of a out of range! " << oor.what() << endl;
flag = false;
}
try
{
b_int = stoi(b, string::size_type());
}
catch (const std::out_of_range &oor)
{
std::cerr << "Error: Value of b out of range! " << oor.what() << endl;
flag = false;
}

ll ans = getAdd(a_int, b_int);

if (!flag)
{
cout << "Try Again." << endl;
continue;
}
else
{
cout << "Correct! The value is " << ans << "." << endl;
}
}

cout << "==============================================" << endl;
return 0;
}
  • 不带格式转换的程序(对照程序)
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
127
128
129
130
/*
** 程序名:testAddCompact
** 程序功能:做testAdd程序的对照组,检测数值类型转换的有效性
** 作者:Edwardzcn
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<string, string> pss;
#define rep(i, a, n) for (int i = a; i < n; ++i)
#define per(i, a, n) for (int i = n - 1; i >= a; --i)

#define DEBUG_INPUT DEBUG_INPUT

/*
** 函数名:isNum
** 函数功能:用来检测是否为合法输入数
** 参数名称与类型:
** string s
** 返回值:
** bool
*/
bool isNum(string s)
{
bool flag = false;
if (s == "")
return false;
rep(i, 0, s.size())
{
if (i == 0 && s[i] == '-')
continue;
if (s[i] < '0' || s[i] > '9')
return false;
}
return true;
}

/*
** 函数名:getAdd
** 函数功能:根据两输入值获取商值
** 参数名称与类型:
** int a
** int b
** 返回值类型:
** long long
*/
ll getAdd(int a, int b)
{
return ll(a + b);
}

/*
** 函数名:main
** 函数功能:主函数,入口
** 参数名称与类型:
** int argc
** char* argv[]
** 返回值类型:
** int
*/
int main(int argc, char *argv[])
{
cout << ("======= Start Program: testAdd ================") << endl;

#ifdef DEBUG_INPUT
freopen("input", "r", stdin);
// freopen("output", "w", stdout);
#endif // DEBUG_INPUT
string a, b;
int parseNum;
while (cin >> a >> b)
{
bool a_type = isNum(a);
bool b_type = isNum(b);
int a_int, b_int;
int flag = true;
// 判断非数字的情况
if (!a_type)
{
flag = false;
std::cerr << "Error: a=" << a << " is not a num." << endl;
}
if (!b_type)
{
flag = false;
std::cerr << "Error: b=" << b << " is not a num." << endl;
}
if (!flag)
{
cout << "Try Again." << endl;
continue;
}

try
{
a_int = stoi(a, string::size_type());
}
catch (const std::out_of_range &oor)
{
std::cerr << "Error: Value of a out of range! " << oor.what() << endl;
flag = false;
}
try
{
b_int = stoi(b, string::size_type());
}
catch (const std::out_of_range &oor)
{
std::cerr << "Error: Value of b out of range! " << oor.what() << endl;
flag = false;
}

ll ans = getAdd(a_int, b_int);

if (!flag)
{
cout << "Try Again." << endl;
continue;
}
else
{
cout << "Correct! The value is " << ans << "." << endl;
}
}

cout << "==============================================" << endl;
return 0;
}

TDD 测试阶段

下面三幅图片分别展示了我完成的情况,开始的 1.0 版本,包含了异常输入的检验,但是并不能进行越界检查。随后通过 try-catch,加入了越界检验,并完成对照程序。

  • 功能不达标,越界报错,(红灯状态)

    运行截图:

    Lab4_1
    Lab4_1

    测例执行情况:

    测例编号 输入 预计输出 实际输出 备注
    1 a=2147483646 b=1 2147483647 2147483647 符合预期
    2 a=2147483647 b=1 2147483647 2147483648 符合预期
    3 a=2147483647 b=2147483647 4294967294 4294967294 符合预期
    4 a=-2147483647 b=-1 -2147483648 -2147483648 符合预期
    5 a=-2147483648 b=-1 -2147483649 -2147483649 符合预期
    6 a=-2147483648 b=-2147483648 -4294967296 -4294967296 符合预期
    7 a=0.005 b = asdfab 2 Error 2 Error 符合预期
    8 a=2147483648 b=-2147483649 2 Error 程序故障 不符合预期
  • 功能达标(绿灯状态)

    运行截图:

    Lab4_2
    Lab4_2

    测例执行情况:

    测例执行情况:

    测例编号 输入 预计输出 实际输出 备注
    1 a=2147483646 b=1 2147483647 2147483647 符合预期
    2 a=2147483647 b=1 2147483647 2147483648 符合预期
    3 a=2147483647 b=2147483647 4294967294 4294967294 符合预期
    4 a=-2147483647 b=-1 -2147483648 -2147483648 符合预期
    5 a=-2147483648 b=-1 -2147483649 -2147483649 符合预期
    6 a=-2147483648 b=-2147483648 -4294967296 -4294967296 符合预期
    7 a=0.005 b = asdfab 2 Error 2 Error 符合预期
    8 a=2147483648 b=-2147483649 2 Error 2 Error 符合预期
  • 对照程序

    运行截图:

    Lab4_3
    Lab4_3
    测例编号 输入 预计输出 实际输出 备注
    1 a=2147483646 b=1 2147483647 2147483647 符合预期
    2 a=2147483647 b=1 2147483647 -2147483648 符合预期,对照程序运算不进行转化
    3 a=2147483647 b=2147483647 4294967294 -2 符合预期,对照程序运算不进行转化
    4 a=-2147483647 b=-1 -2147483648 -2147483648 符合预期
    5 a=-2147483648 b=-1 -2147483649 2147483647 符合预期
    6 a=-2147483648 b=-2147483648 -4294967296 0 符合预期,对照程序运算不进行转化
    7 a=0.005 b = asdfab 2 Error 2 Error 符合预期
    8 a=2147483648 b=-2147483649 2 Error 2 Error 符合预期

重构代码已经进行过,上一节代码中可以体现,另外我觉得重构必然导致回归测试,否则对于软件产品上线运行是极不负责任的。

实验总结

本次实验完全按照 TDD 的过程进行程序(项目)设计,先构建样例,再构建程序,非常深刻的感受到 TDD 作为敏捷开发的一个分支的特点和优劣势。

在本次实验中还同时借边界测试温习了一下源码补码相关的内容。测试样例的编写由于构建测例在先,使用偏向黑盒的测例构造方法(注重功能,强调试错),同步做了一个对照程序来确认验证程序的突出性质。