从上一节所介绍的单元测试过程中,我们已经了解到单元测试的大体过程。在本节 中,将根据两票系统项目的实际情况,以该项目的一个单元测试为例来说明如何进行单 元测试。当然,这里忽略了单元测试过程中的很多细节,只介绍单元测试过程中的关键 环节,如:根据被测试单元的实际情况制定的单元测试计划;测试分析和设计;测试的 执行等方面。
1.单元测试计划
(1)简介。这份文档的目标是详细描述对两票系统的可以实现在二次系统图上进行图 形开票模块的功能验证的测试过程。本文档所关注的特征来自于需求文档,包括二次图模 块和需求定义。需求文档的标识是 TwoBill-RD-02,它在文档控制系统中,读者可以从如 下 的 网 址 中 获 得 。 http://www.tmtcointernal. com/usr/ www/docstores/design/requirements/
TwoBill-RD-02.doc。
(2)本测试的总目标。测试该模块是否实现了需求文档中定义的所有功能。
(3)完整性需求。在测试该模块是否实现了需求文档中定义的所有功能之前,应该做如 下两项工作①测试数据初始化是否无误;②测试二次图开票 GUI 界面是否与系统维护模块显 示一致。
(4)单元测试终止标准。
1)硬件资源不足或故障造成软件无法运行。
2)软件运行后无法正确显示(如:因数据初始化有误造成 GUI 界面同系统维护模块显示 不一致或不正确等)。
3)所有功能测试均已经完成。
(5)资源需求。
1)软件资源。
操作系统:Windows 2000 Web 服务器:Tomcat 5.0
数据库服务器:SQL Server 2000 浏览器:Microsoft IE 6.0
测试工具:Junit
2)硬件资源。同开发用 PC 机配置相同即可。
3)测试进度。如表 3-3 所示。
表 3-3 二次系统图图形开票模块
任务 开始日期 结束日期
配置并调试测试环境 10/01 10/03
GUI 界面测试 10/04 10/06
所有功能测试 10/07 10/10
(6)准备测试的特征。以下特征将被测试,以确保该模块能满足需求规格说明书中指定 的需求:
需求 2.2.1 用户界面 需求 2.2.2 弹出菜单 需求 2.2.3 图形开票
(7)不准备测试的特征。本次测试将不考虑是否能够同一次系统图图形开票模块的集成。
(8)测试方法。该单元测试方法包括功能测试、GUI 测试和数据测试。
z 功能测试:对需求定义中所描述的所有功能进行测试。
z GUI 测试:我们将使用 Microsoft IE 对图形用户界面进行测试,对所有功能以及 功能间的切换进行测试,对于弹出菜单的测试借助 IBM Rational Functional Tester 工具进行。
z 数据测试:对绘制二次系统图的初始化数据进行测试,主要借助 Junit 测试工具来 实现。
(9)通过/失败标准。每个测试用例的通过/失败标准都由它预期的结果来描述。如果在 执行一个测试用例时得到了预期的结果,那么测试就通过了。如果在执行一测试用例时没有得 到预期的结果,那么测试就失败了。如果因为构建中存在一些阻碍性缺陷而未能执行某项测试,
则该测试的结果将记为“受阻”。
要让该模块退出单元测试阶段,需要至少在软件的一个构件上运行本测试计划定义的所 有测试用例。所有运行的测试都要通过,在测试结束时不能有未修复的灾难级错误。
(10)测试结束后须提交的产物。包括以下几个文档:
z 本测试计划。
z 测试规格说明文档。
z 测试结果报告。
z 向测试经理和开发经理提交的每日测试状态报告。
z 缺陷报告。
(11)测试执行人员。由开发人员来执行该模块的测试。
(12)风险和应急计划。如果在执行该模块的测试之前,二次系统图的图片可能还没有 完全准备好,那么测试负责人将参加其他模块的开发设计会议。
2.测试的设计和开发
前面已经提到过,测试设计和开发要以测试计划作为输入。在设计测试时,首先应该明 确测试目标(细化测试的方法和范围);确定每个测试的输入规格说明;确定每个测试使用的 测试配置;复查测试设计的覆盖率和测试的准确度。
在对本单元进行测试时所采用的方法是:先完成黑盒测试,然后统计白盒覆盖率,
再针对未覆盖的逻辑单位设计测试用例覆盖它,例如,先检查是否有语句未覆盖,有的 话设计测试用例覆盖它,然后用同样方法完成条件覆盖、分支覆盖和路径覆盖。这样,
既检验了黑盒测试的完整性,又避免了重复的工作,达到了非常高的测试完整性。不过,
这些工作可不是手工能完成的,必须借助于工具。用于白盒测试的工具有很多种,读者 可参考第 7 章的相关内容。
然后,就应该开发测试用例,应该注意的是在测试用例中应该尽可能详细地描述测试过
程,对于耗时的测试进行自动化。
最后,验证和调试测试。
下面详细介绍该阶段的具体过程,drawbh 类的代码如下。
import java.awt.*;
import java.awt.Graphics;
import java.io.*;
import java.text.*;
import java.awt.Image;
import java.awt.image.*;
import java.awt.event.*;//为了利用鼠标事件 import java.applet.Applet;//为了利用 Applet import java.lang.*;
import java.net.*;
import java.sql.*;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
public class drawbh extends JApplet
implements MouseListener, MouseMotionListener,ItemListener,ActionListener{
… …
db_Czp datadb=new db_Czp();//数据连接 ResultSet rs=null;
String sqlString=null;
String bds_tablename;//所选用的表 bds_tablename="bds20";
int idx_bhdyname;//mydata_bh 数组维数 int idx_bhbhname;
int idx_bhkname;//mydata_bhk 数组维数
MyData_bhk mydata_bhk[][]=new MyData_bhk[arrSize1][arrSize2];
MyData_bh mydata_bh[][]=new MyData_bh[arrSize1][arrSize2];
int bh;
int bh1;
int bhk;
…
public void init_bh(){//保护数据初始化 try{
sqlString="select * from " +bds_tablename + "_abh";
rs=datadb.executeQuery(sqlString);
idx_bhdyname=0;
idx_bhbhname=0;
mydata_bh[idx_bhdyname][idx_bhbhname]=new MyData_bh();
if(rs.next()){
mydata_bh[idx_bhdyname][idx_bhbhname].setidx(rs.getInt("idx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setflagidx(rs.getInt("flagidx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setx(rs.getInt("x"));
mydata_bh[idx_bhdyname][idx_bhbhname].setY(rs.getInt("y"));
mydata_bh[idx_bhdyname][idx_bhbhname].setbdsname(rs.getString("bdsname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setdyname(rs.getString("dyname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setBHname(rs.getString("bhname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setBhkindidx(rs.getInt("bhkindidx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setstation(rs.getInt("station"));
mydata_bh[idx_bhdyname][idx_bhbhname].setCzRul0(rs.getString("czrul0"));
mydata_bh[idx_bhdyname][idx_bhbhname].setCzRul1(rs.getString("czrul1"));
bh=mydata_bh[0][0].getidx();
} … …
for(int idx_bhdyname=1;idx_bhdyname<=bh;idx_bhdyname++){
if(rs.next()){
idx_bhbhname=0;
mydata_bh[idx_bhdyname][idx_bhbhname]=new MyData_bh();
mydata_bh[idx_bhdyname][idx_bhbhname].setidx(rs.getInt("idx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setflagidx(rs.getInt("flagidx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setx(rs.getInt("x"));
mydata_bh[idx_bhdyname][idx_bhbhname].setY(rs.getInt("y"));
mydata_bh[idx_bhdyname][idx_bhbhname].setbdsname(rs.getString("bdsname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setdyname(rs.getString("dyname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setBHname(rs.getString("bhname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setBhkindidx(rs.getInt("bhkindidx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setstation(rs.getInt("station"));
mydata_bh[idx_bhdyname][idx_bhbhname].setCzRul0(rs.getString("czrul0"));
mydata_bh[idx_bhdyname][idx_bhbhname].setCzRul1(rs.getString("czrul1"));
//while(rs.next())
bh1=mydata_bh[idx_bhdyname][0].getidx();
}
for(idx_bhbhname=1;idx_bhbhname<=bh1;idx_bhbhname++) {
if(rs.next()){
mydata_bh[idx_bhdyname][idx_bhbhname]=new MyData_bh();
mydata_bh[idx_bhdyname][idx_bhbhname].setidx(rs.getInt("idx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setflagidx(rs.getInt ("flagidx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setx(rs.getInt("x"));
mydata_bh[idx_bhdyname][idx_bhbhname].setY(rs.getInt("y"));
mydata_bh[idx_bhdyname][idx_bhbhname].setbdsname(rs.getString ("bdsname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setdyname(rs.getString ("dyname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setBHname(rs.getString ("bhname"));
mydata_bh[idx_bhdyname][idx_bhbhname].setBhkindidx(rs.getInt ("bhkindidx"));
mydata_bh[idx_bhdyname][idx_bhbhname].setstation(rs.getInt ("station"));
mydata_bh[idx_bhdyname][idx_bhbhname].setCzRul0(rs.getString ("czrul0"));
mydata_bh[idx_bhdyname][idx_bhbhname].setCzRul1(rs.getString ("czrul1"));
e.printStackTrace();
}
rs=datadb.executeQuery(sqlString);
idx_bhkname=0;
idx_bhcz=0;
if(rs.next()){
mydata_bhk[idx_bhkname][idx_bhcz]=new MyData_bhk();
mydata_bhk[idx_bhkname][idx_bhcz].setidx(rs.getInt("idx"));
mydata_bhk[idx_bhkname][idx_bhcz].setflagidx(rs.getInt("flagidx"));
mydata_bhk[idx_bhkname][idx_bhcz].setname(rs.getString("name"));
mydata_bhk[idx_bhkname][idx_bhcz].setczts(rs.getString("czts"));
mydata_bhk[idx_bhkname][idx_bhcz].setcznr(rs.getString("cznr"));
mydata_bhk[idx_bhkname][idx_bhcz].setState(rs.getInt("state"));
mydata_bhk[idx_bhkname][idx_bhcz].setBMPname (rs.getString("bmpname"));
//while(rs.next())
bhk=mydata_bhk[0][0].getidx();
}
for(int idx_bhkname=1;idx_bhkname<=bhk;idx_bhkname++){
for(int idx_bhcz=0;idx_bhcz<=10;idx_bhcz++){
if(rs.next()){
mydata_bhk[idx_bhkname][idx_bhcz]=new MyData_bhk();
mydata_bhk[idx_bhkname][idx_bhcz].setidx(rs.getInt("idx"));
mydata_bhk[idx_bhkname][idx_bhcz].setflagidx(rs.getInt("flagidx"));
mydata_bhk[idx_bhkname][idx_bhcz].setname(rs.getString("name"));
mydata_bhk[idx_bhkname][idx_bhcz].setczts(rs.getString("czts"));
mydata_bhk[idx_bhkname][idx_bhcz].setcznr(rs.getString("cznr"));
mydata_bhk[idx_bhkname][idx_bhcz].setState(rs.getInt("state"));
mydata_bhk[idx_bhkname][idx_bhcz].setBMPname (rs.getString("bmpname"));
}
} }
datadb.closeResultSet(rs);
}
catch(SQLException e) {
e.printStackTrace(); } }
public void init_bhscreen(){//保护屏列表初始化
for(int i=1;i<=bh;i++){
bhscreen.add(mydata_bh[i][0].getdyname());
} }
public boolean SearcBhidx(int xe,int ye){//判断选中的是哪一个保护(bhname) boolean isinrect =false;
int j;
double x1,y1;
double x2,y2;
for(j=1;j<=mydata_bh[index+1][0].getidx();j++){
x1=mydata_bh[index+1][j].getx();
y1=mydata_bh[index+1][j].getY();
String pictureName;
pictureName=mydata_bhk[mydata_bh[index+1][j].getBhkindidx()]
[mydata_bh[index+1][j].getstation()].getBMPname();
try{
int num=pictureName.indexOf(".");
if(num!=-1){
String str3;
str3=pictureName.substring(0,num);
img=getImage(getCodeBase(),"image/"+str3+".gif");
}
w=img.getWidth(this);
h=img.getHeight(this);
//double pictureBl;
if(xe>(x1+250)&&xe<x2&&ye>(y1+50)&&ye<y2){
ThisBHidx=j;
isinrect =true;
} }
catch(NullPointerException e){}
}
ThisBHKindidx = mydata_bh[index+1][ThisBHidx1].getBhkindidx();
int station;
station = mydata_bh[index+1][ThisBHidx1].getstation();
//Thisstate = station;
for(i=1;i<=mydata_bhk[ThisBHKindidx][0].getidx();i++){
item[i].setVisible(true);
item[i].setText(mydata_bhk[ThisBHKindidx][i].getczts());
//'判断菜单是否有效
if(mydata_bhk[ThisBHKindidx][i].getState()==0){//'不改变状态 if(mydata_bhk[ThisBHKindidx][i].getBMPname().equals(mydata_bhk [ThisBHKindidx][station].getBMPname()))
item[i].setEnabled(true); (mydata_bhk[ThisBHKindidx][station].getBMPname()))
item[i].setEnabled(true);
else
item[i].setEnabled(false);
}
}
item[mydata_bhk[ThisBHKindidx][0].getidx()+1].setVisible(true);
item[mydata_bhk[ThisBHKindidx][0].getidx()+1].setText("取消");
for(int j=mydata_bhk[ThisBHKindidx][0].getidx()+2;j<=14;j++){
item[j].setVisible(false);
popmenu.add(item[mydata_bhk[ThisBHKindidx][0].getidx()+1]);
popmenu.validate();
从上述的代码中,可以看到这是一个 Applet,因此需要嵌入到 HTML 文件中依赖浏览器 才能够运行,进而显示运行界面。为了对该类进行功能测试,我们编辑了一个简单的 HTML 文件,即:
<html>
<body>
<applet code=" applet.unedu.tlpe.drawbh.class" width=1000 height=800> </applet>
</body>
</html>
然后,双击并运行该文件,得到的界面如图 3-7 所示。
图 3-7 显示弹出菜单的二次系统图界面
该类所实现的功能如下:显示某个分厂(通过 bds_tablename 变量来确定)的二次系统图,
可以实现图形开票功能,例如:在二次系统图界面选中了储能电源单击右键后,界面如图 3-7 所示,然后选择“合上”菜单项,储能开关由拉开状态(如图 3-8 所示)转换成合上状态(如 图 3-9 所示)。同时,将操作内容(1.合上滨西甲线储能电源开关)写入操作票内容显示和文 字编辑界面(如图 3-10 所示)。
首先,确定对该模块所进行的黑盒测试,所进行的测试项如下。
(1)界面测试。
1.1 测试在保护屏列表中所显示的名称是否正确。
1.2 测试二次系统图显示是否正确,如:当选择了滨西甲线后,在界面的右侧区域是否正 常显示了与该保护屏相对应的元件,即:如图 3-7 所示的“操作直流保险器”、“信号直流保险 器”、“储能电源开关”、“驱潮加热开关”。
1.3 测试选中每类元件单击右键后,能否显示弹出菜单,以及所显示的弹出菜单项是否 正确。
图 3-8 储能电源开关位于“拉开”状态的二次系统图界面
图 3-9 储能电源开关位于“合上”状态的二次系统图界面
(2)功能测试。
2.1 测试生成的操作内容是否和预期结果一致。如:在图 3-7 中选中了“储能电源开关”
单击右键,然后选择“合上”菜单项后,写入操作票内容显示和文字编辑界面的内容是否为:
“1.合上滨西甲线储能电源开关”。
2.2 操作结束后,元件的状态是否按照相应的操作进行转换。如:在处于“拉开”状态的
“储能电源开关”上执行了“合上”操作后,“储能电源开关”是否处于“合上状态”。
为了进行上述所列出的测试内容,设计如下测试用例。
图 3-10 操作票子系统的操作票内容显示和文字编辑界面
1.1 项测试用例
由于电厂的每个分厂都包括多个系统图,而每个系统图又包含多个保护屏,因此我们不可 能对所有数据组合进行测试,也没有必要进行这样的测试。在进行该项测试时,采用边界值分 析和等价类划分的方法首先设计一个测试用例,如表 3-4 所示。
表 3-4 界面测试用例 1
序号 输入 预期输出
Test case 1 bds_tablename=“bds10” 保护屏列表的第一项显示“滨西甲线”、第二项显示“滨西乙线”、 第五项显示“一号主变保护屏”、倒数第二项显示“东电容器 350”、最后一项显示“10 千伏西母电压互感器”
之所以选取这些数据作为测试用例,基于的考虑如下:表示第一、二个保护屏和倒数第一、
二个保护屏名称的数据位于数据库中的边界,从而可以判断是否从数据库中提取了所有表示保 护屏名称的数据并正确显示在保护屏列表的相应位置。另外随机选取了一个位于数据库的中间 部分的数据,作为补充测试数据。
1.2 项测试用例
每个保护屏所包含的元件是不同的,同样使用等价类和边界值分析的方法进行二次图页 面显示的测试,分别针对第一、二、五和倒数第一、二保护屏设计了测试用例,测试用例如表 3-5 所示。
字典区
表 3-5 界面测试用例 2
序号 前提 输入 预期输出
Test case 1 bds_tablename=“bds10” 选择保护屏列表的第一项,即
“滨西甲线”
“滨西甲线”