前言
之所以开设“前人种树”专栏,是因为笔者十分推崇某位牛马前辈,当初刚入学对实验一筹莫展时偶然接触到这位前辈分享的材料,且不说对我有没有帮助,反正从那以后我经常看他分享的实验材料。慢慢地看多了之后才发现,原来这位前辈发材料的初衷可能没有我想的那么高尚,不是为了作为前人种树让后来人乘凉的,纯纯为了炫耀。为什么这么说呢?因为基本上他所有的公开分享的材料,都是要么缺斤少两,要么就在某个细节上把原来正确的改成错的。而他收费恰米的材料,都是完全正确可以直接拿来用的。
所以我呢,就把自己的实验材料也做个分享,不缺斤少两,更不故意把对的改成错的让后面的学弟们找半天,故意捉弄人嘛这不是?【楽】希望我这个种树的前人能对后人们有些许帮助,也希望各位是在弄懂我的东西后才去套用甚至自创。
说远了,这个课程设计实验分成两大部分,第一大部分又分成两部分(白盒测试+黑盒测试),第二大部分是自动化测试工具“IBM Rational Functional Tester”(简称RFT)的使用。
其中,第一大部分的白盒测试要求利用基本路径测试法对一个系统进行主要功能的测试,而且要求测试的必须是非本人开发的系统,我用的是同学的数据库课程设计的“图书馆信息管理系统”,白盒测试要求设计好测试样例后,根据待测系统构建单元测试环境,并自行选择自动化测试工具进行测试;
黑盒测试又分成两部分,一个是要用因果图法设计测试样例,另一个要求用等价类划分法结合边界值法,黑盒测试部分只要求生成测试样例后自己手动测试即可。
设计测试样例的过程要拍照截图贴到实验报告里,比如第一部分的控制流图(带输入条件约束)、第二部分的因果图、判定表、等价类覆盖表啥的。
第二大部分需要注意的点看下文吧,挺详细的了。。
······
后记:很遗憾,这个实验笔者没有拿到优,甚至连良都没有,只有一个惨淡的中。。至于够不够格评优,笔者不想多费口舌自辩或自夸些什么,报告就在下文,挺详细,看官们自行判断自行把握。笔者在验收之后还来给前言贴上个后记,目的是为了给后来人提个醒,软件测试这门课别选某单名的女性教师,如果真的倒霉,当学期只能选这位老师的话,想拿优,你就千万平时在课上就要给她留下好印象,坐到前排她面前去,好好听课,下课多单独提问,并且期末考试考个高分。如此种种皆满足你才有机会在实验拿优。这么一看,这老师貌似还不错,是个高标准严格要求学生的!非也,我的这段话你再细品品,其内大有玄机。只能说:女人嘛,天生是感性动物,哪怕做了理工科教师,也摆脱不了天性,反正你跟她混熟、关系混好点,就对了。
(关于为什么期末考试要考个高分才行?实验不是跟正课分开评成绩的吗?
在笔者的眼光看来,我们那一批60+人的实验报告成绩,似乎跟实验过程质量、报告质量好坏无关,反而,跟期末考试成绩成极大关联关系!这实在难以不令人产生无端的恶意揣测:该老师可能拖延症发作,导致迟迟没有批改,眼看快到ddl了这才匆匆胡乱批改实验报告,毕竟确实量大。实验成绩似乎是直接瞄着期末考试成绩、以及看平时哪个学生表现好跟她混得熟,给的。
巧的是,当年的该课实验成绩确实很晚很晚才出。。。)
这有点阴谋论了,主要是笔者也被实实在在地坑过,所以才会有此阴暗揣测,后人们别学,当个笑话看就好。如果真的诽谤冤枉到了那位单名教师,笔者愿意在此博文下诚挚道歉并且删除相关言论。
笔者在此再次强调:把这些想法写出来的目的就不是为了告诉后人们世界有多阴暗,而是为了让大家脚踏实地该做的就都落实到位,自身实力够硬,自然一力降十会不必担心那些乱七八糟的负面影响了。就像上述例子中,如果笔者期末考试成绩分数也是95+,那实验成绩大概率是优的。
第一大部分 “图书馆信息管理系统”的测试
(1)白盒测试
(2.1)黑盒测试-等价类划分法结合边界值法
(2.2)黑盒测试-因果图法
(3)待测系统主要功能源代码+测试代码
//附录(设计流程图、程序、表格、数据等)
//代码段一:
private void button1_Click(object sender, EventArgs e)
{
if(textBox1.Text !=""&& textBox2.Text !="")
{
Login();
}
else
{
MessageBox.Show("输入有空项,重新输入");
}
}
//登陆方法,判断是否可以登陆
public void Login()
{
//用户
if(radioButtonUser.Checked==true)
{
Dao dao = new Dao();
string sql = "select * from t_user where id='"+textBox1.Text+"' and psw='"+textBox2.Text+"'";
IDataReader dc = dao.read(sql);//连接数据库
string checkUserSql = "SELECT COUNT(*) FROM t_user WHERE id='" + textBox1.Text + "'";
int userCount = Convert.ToInt32(dao.ExecuteScalar(checkUserSql));
if (dc.Read())
{
Data.UID = dc["id"].ToString();
Data.UName = dc["name"].ToString();
MessageBox.Show("登陆成功");
user1 user = new user1();//跳转新窗体
this.Hide();
user.ShowDialog();
this.Show();
}
else
{
if (userCount>0)
{
MessageBox.Show("密码错误");
}
else
{
MessageBox.Show("用户未注册");
}
}
dao.DaoClose();
}
//管理员
if (radioButtonManager.Checked == true)
{
Dao dao = new Dao();
string sql = "select * from t_manager where id='" + textBox1.Text + "' and psw='" + textBox2.Text + "'";
IDataReader dc = dao.read(sql);//连接数据库
string checkUserSql = "SELECT COUNT(*) FROM t_manager WHERE id='" + textBox1.Text + "'";
int userCount = Convert.ToInt32(dao.ExecuteScalar(checkUserSql));
if (dc.Read())
{
MessageBox.Show("登陆成功");
manager1 manager = new manager1();//跳转新窗体
this.Hide();
manager.ShowDialog();
this.Show();
}
else
{
if (userCount > 0)
{
MessageBox.Show("密码错误");
}
else
{
MessageBox.Show("用户未注册");
}
}
dao.DaoClose();
}
}
//代码段二:
private void button1_Click(object sender, EventArgs e)
{
string id = dataGridView1.SelectedRows[0].Cells[0].Value.ToString();
int number = int.Parse(dataGridView1.SelectedRows[0].Cells[4].Value.ToString());
if (number < 1)
{
MessageBox.Show("库存不足");
}
else
{
string sql = $"insert into t_lend ([uid],bid,[datetime]) values('{Data.UID}','{id}',getdate());update t_book set number=number-1 where id='{id}'";
Dao dao = new Dao();
if (dao.Execute(sql) > 1)//两条sql
{
MessageBox.Show($"用户{Data.UName}借出了图书{id}");
Table();
}
}
}
//代码段三:
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text != "" && textBox2.Text != "" && textBox3.Text != "" && textBox4.Text != "" && textBox5.Text != "")
{
Dao dao = new Dao();
string sql = $"insert into t_book values('{textBox1.Text}','{textBox2.Text}','{textBox3.Text}','{textBox4.Text}',{textBox5.Text})";
try//使用 try 和 catch 块捕获 SqlException 异常,检查其错误码是否为 2627,这是唯一性冲突的错误码。
{
int n = dao.Execute(sql);//添加进数据库,返回int变的个数
if (n > 0)
{
MessageBox.Show("添加成功");
}
else
{
MessageBox.Show("添加失败");
}
}
catch (System.Data.SqlClient.SqlException ex)
{
// 处理唯一性冲突异常
if (ex.Number == 2627) // 2627 是唯一性冲突的错误码
{
MessageBox.Show("添加失败,书号重合");
}
else
{
MessageBox.Show($"添加失败,发生数据库错误: {ex.Message}");
}
}
textBox1.Text = "";
textBox2.Text = "";
textBox3.Text = "";
textBox4.Text = "";
textBox5.Text = "";
}
else
{
MessageBox.Show("添加失败,存在输入为空");
}
}
//代码段a:
//测试代码(因为格式类似,所以仅列出测试样例①的测试代码,省略样例②~⑧):
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using 管理系统; //因为待测系统的项目解决方案名就叫“管理系统”
namespace Login.Test
{
[TestClass]
public class login_Test
{
login L = new login();
[TestMethod]
public void LoginTestMethod1()
{
parse("1", "1", 1); //测试样例1
//采用断言进行比较并输出测试结果
L.start();
Assert.AreEqual("登陆成功",L.loginInfo);//前者是预期结果,后者是实际运行结果
L.Close();
}
······
}
}
//调用的桩程序:
public void parse(string str1,string str2,int id)
{
L.textBox1_setText(str1); //账户
L.textBox2_setText(str2); //密码
if (id == 1) //用户登录
L.setRadioButton_User(); //选中用户按钮
if (id == 2) //管理员登录
L.setRadioButton_Manager(); //选中管理员按钮
}
}
}
public void start()
{
if (textBox1.Text != "" && textBox2.Text != "")
{
Login(); //登录到相应页面
}
else
{
MessageBox.Show("输入有空项,重新输入");
loginInfo = "输入有空项,重新输入";
}
}
//代码段b:
//测试代码:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Data;
using 管理系统;
namespace userBorrow_Test
{
[TestClass]
public class userBorrow_Test
{
user2 U = new user2();
[TestMethod]
public void userBorrow_TestMethod1() //测试样例1
{
U.start("002","002","1"); //1号图书目前库存数为1,预期成功借出
Assert.AreEqual($"用户{Data.UName}借出了图书", U.borrowInfo);
U.Close();
}
[TestMethod]
public void userBorrow_TestMethod2() //测试样例2
{
U.start("002", "002", "1"); //1号图书目前库存数为0,预期借出失败
Assert.AreEqual("库存不足", U.borrowInfo);
U.Close();
}
}
}
//调用的桩程序:
public void start(string str1,string str2,string x)
{
Data.UID = str1;
Data.UName = str2;
string id = x;
Dao dao2 = new Dao();
string sql2 = "select number from t_book where id = '"+id+"'";
IDataReader dc = dao2.read(sql2);
dc.Read();
int number = (int)dc[0];
if (number < 1)
{
MessageBox.Show("库存不足");
borrowInfo = "库存不足";
}
else
{
string sql = $"insert into t_lend ([uid],bid,[datetime]) values('{Data.UID}','{id}',getdate());update t_book set number=number-1 where id='{id}'";
Dao dao = new Dao();
if (dao.Execute(sql) > 1)//两条sql
{
MessageBox.Show($"用户{Data.UName}借出了图书{id}");
borrowInfo = $"用户{Data.UName}借出了图书";
Table();
}
}
}
//代码段c:
//测试代码(因为格式类似,所以仅列出测试样例①的测试代码,省略样例②~⑥):
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using 管理系统;
namespace managerAdd_Test
{
[TestClass]
public class managerAdd_Test
{
manager21 M = new manager21();
[TestMethod]
public void managerAdd_TestMethod1()
{
parse("1000", "1000", "1000", "1000", "2"); //测试样例1
M.start(); //调用桩程序
Assert.AreEqual("添加成功", M.addInfo);//前者是预期结果,后者是实际运行结果
M.Close();
}
······
}
}
//调用的桩程序:
public void parse(string str1,string str2,string str3,string str4,string str5)
{
M.textBox1_setText(str1); //书号
M.textBox2_setText(str2); //书名
M.textBox3_setText(str3); //作者
M.textBox4_setText(str4); //出版社
M.textBox5_setText(str5); //库存
}
public void start()
{
if (textBox1.Text != "" && textBox2.Text != "" && textBox3.Text != "" && textBox4.Text != "" && textBox5.Text != "")
{
Dao dao = new Dao();
string sql = $"insert into t_book values('{textBox1.Text}','{textBox2.Text}','{textBox3.Text}','{textBox4.Text}',{textBox5.Text})";
try//使用 try 和 catch 块捕获 SqlException 异常,检查其错误码是否为 2627,这是唯一性冲突的错误码。
{
int n = dao.Execute(sql);//添加进数据库,返回int变的个数
if (n > 0)
{
MessageBox.Show("添加成功");
addInfo = "添加成功";
}
else
{
MessageBox.Show("添加失败");
addInfo = "添加失败";
}
}
catch (System.Data.SqlClient.SqlException ex)
{
// 处理唯一性冲突异常
if (ex.Number == 2627) // 2627 是唯一性冲突的错误码
{
MessageBox.Show("添加失败,书号重合");
addInfo = "添加失败,书号重合";
}
else
{
MessageBox.Show($"添加失败,发生数据库错误: {ex.Message}");
addInfo = "添加失败,发生数据库错误";
}
}
textBox1.Text = "";
textBox2.Text = "";
textBox3.Text = "";
textBox4.Text = "";
textBox5.Text = "";
}
else
{
MessageBox.Show("添加失败,存在输入为空");
addInfo = "添加失败,存在输入为空";
}
}
第二大部分 (自动化测试工具IBM Rational Functional Tester的使用)
除了实验任务书,老师会发的资料就是左图的rftlab压缩包,解压出来如右图,其中三个带有图标的可执行文件是用来安装RFT连接项目到C盘的,到时候安装到机房的电脑就行(因为RFT在机房电脑上已经安装好了,这部分实验都是在机房电脑上做的),其余的PDF文件中带conv_overprint后缀的没用,只是个封皮图案,有用的是stuman和stuwrk,比较操蛋的一点就是他们都是全英文的,前者是完整的电子书,后者是把电子书里面的所有实验模块整理出来。所以,做实验的话,建议直接看stuwrk,直接跟着上面的步骤一步步来就行,边做边理解。
其实做到最后你就会发现这部分的实验就是:1.录制你想让计算机待会自动执行的动作流程+2.在录制过程中插入一些必要的验证点、注释啥的+3.录制结束后就可以回放刚刚录好的脚本,回放就是让计算机自动执行你刚刚录制的所有动作流程。
需要特别注意:录制过程中不要做任何待会想让计算机自动执行的操作之外的操作,比如你录到一半你心血来潮点开微信回个消息。否则回放的时候也会做这个动作,这时候你可能已经把微信关了,计算机就找不到微信,点不开,就会一直卡在这里,然后超时,报错,测试不通过。
笔者觉得有用的部分截图贴下面了: