第七章 TestNG 测试框架
一、 什么是测试框架
?
自动化测试框架是由一个或多个自动化测试基础模块、自动化测试管理模块、自动
化测试统计模块等组成的工具集合。
二、 搭建
TestNG
框架环境
1
什么是
TestNG
?
是一个开源自动化测试框架,
TestNG
表示下一代。
?
用于设置测试前的准备代码,测试代码,测试完毕后的处理代码。
2
安装
testng-6.14.zip
?
离线安装
?
(先断网)帮助→安装新软件→添加,位置选择
achive
,选择
zip
文件即可。
?
网络安装
?
帮 助 → 安 装 新 软 件 → 联 网 安 装
testing
,
Work with
下 输 入 :
http://beust.com/eclipse
→添加→勾选
TestNG
三、 使用
TestNG
中注解进行测试
1
什么是注解
?
表示“在某些阶段必然被调用的代码”;
?
下面的注解一般用于修饰测试方法,测试方法名随意。
2
常用注解
2.1 @BeforeClass
注解
?
在调用当前类的第一个测试方法之前运行。
@BeforeClass
public void setup() throws Exception {
System.setProperty("webdriver.ie.driver", "C:\\...\\IEDriverServer.exe");
driver= new InternetExplorerDriver();
driver.manage().timeouts().implicitlyWait(8, TimeUnit.SECONDS);
}
2.2 @AfterClass
注解
?
在调用当前类的最后一个测试方法之后运行。
@AfterClass
public void tearDown() throws Exception {
driver.quit();
}
2.3 @BeforeMethod
注解
?
在每个测试方法之前运行。
2.4 @AfterMethod
注解
?
在每个测试方法之后运行。
2.5 @Test(
属性
1=
值
1
,属性
2=
值
2
,
...)
注解
?
用于修饰测试方法,表示要对被测试类的某个或某些方法进行测试。
?
属性
?
description="
测试描述
"
?
priority=
优先级(从
0
开始)
?
enabled=false
(
false
表示忽略测试)
?
timeOut=?ms
(限时测试,一旦测试的内容运行超过了该时间长度,那么将会
终止,同时标记为
failed
)
?
dataProvider="Dataprovider
的名称或方法名
"
?
dataProviderClass=
产生测试数据的类
2.6 @DataProvider(name="
参数集名
")
注解
?
用于修饰获得参数的方法。
?
此方法用于生成测试数据。
?
@Listeners({TestReport.class})
//
监听测试报告类
3
断言
?
用来断定程序中是否存在缺陷。
3.1 Assert.assertTrue(boolean
型结果
)
?
写法类似于正则表达式,使用更方便。
?
Boolean rs=driver.findElement(By.tagName("body")).getText().contains("Welcome
to the Web Tours site");
?
Assert.assertTrue(rs);
3.2 Assert.assertEquals(
实际结果,预期结果
)
?
用于测试期望结果的断言,即测试两个对象是否相等。
?
String actual=driver.getTitle();
?
String expected="Web Tours";
?
Assert.assertEquals(actual, expected);
4
参数化
4.1 @DataProvider(name="
测试数据集名
")
注解
?
name
项可省略,括号无所谓
4.2
创建获取参数的方法
@DataProvider
public Object[][] getParam() throws Exception{
Object data[][]={ {"London","Paris"},{"Denver","London"},{"Paris","Denver"} };
return data;
}
4.3
使用
dataProvider
访问参数
@Test(description="
订票
",priority=2,dataProvider="getParam")
public void book(String cong , String dao){
new Select(drv.findElement(By.name("depart"))).selectByVisibleText(cong);
new Select(drv.findElement(By.name("arrive"))).selectByVisibleText(dao);
Boolean
rs=drv.findElement(By.tagName("body")).getText().contains("leaves
"+cong+" for "+dao);
assertTrue(rs);
drv.findElement(By.name("Book Another")).click();
}
4.4
不使用列表获取文件中的参数
@DataProvider
public Object[][] getParam() throws Exception{
File file=new File("C:\\...\\flights.txt");
//
打开文件,计算文件行数
FileReader bytes=new FileReader(file);
BufferedReader chars=new BufferedReader(bytes);
String row=null;
int lineCount=0;
while((row=chars.readLine())!=null)
lineCount++;
chars.close();
//
重新打开文件,读取每行
bytes=new FileReader(file);
chars=new BufferedReader(bytes);
Object[][] data=new Object[lineCount][];
int i=0;
while((row=chars.readLine())!=null){
String cols[]=row.split("\t");
data[i++]=cols;
}
//
测试查看每行
/*
for(Object x[]:data){
for(Object y:x)
System.out.print(y+" ");
System.out.println();
}
*/
chars.close();
return data;
}
4.5
分离获得参数的类
?
定义获得参数的类
public class Param{
@DataProvider(name=?)
public Object[][] getParam() throws Exception{
...
}
}
?
引用参数类中的参数
@Test(description="
订票
",priority=2,dataProvider="getParam",dataProviderClass=
不
加引号的其他类名
.class
public void book(String cong , String dao){
new Select(drv.findElement(By.name("depart"))).selectByVisibleText(cong);
new Select(drv.findElement(By.name("arrive"))).selectByVisibleText(dao);
Boolean
rs=drv.findElement(By.tagName("body")).getText().contains("leaves
"+cong+" for "+dao);
assertTrue(rs);
drv.findElement(By.name("Book Another")).click();
}
4.6
使用列表获取文件中的参数
@DataProvider
public Object[][] getParam() throws Exception{
List<String[]> rows=new ArrayList<String[]>();
File file=new File("C:/.../flights.txt");
FileReader reader=new FileReader(file);
BufferedReader buffer=new BufferedReader(reader);
String row=null;
while((row=buffer.readLine())!=null){
String columns[]=row.split("\t");
rows.add(columns);
}
reader.close();
Object[][] data=new Object[rows.size()][];
for(int i=0;i<rows.size();i++)
data[i]=rows.get(i);
return data;
}
5
生成测试报告
?
测试报告默认存储位置
?
Java
项目名
\test-output
?
可以自己编写并优化
TestReport.java
?
测试类中添加监听器
@Listeners({TestReport.class})
即可
?
class
是固定关键字
?
放到测试类名的上一行
@Listeners({TestReport.class})
//
监听测试报告类
public class WebTest {
...
}
6
测试套件
6.1
测试
test
一个
test
可以包含一系列的测试方法,测试方法由
@Test
注解。
6.2
测试套件
suite
一个
suite
可以包含多个独立的
test
。
6.3
定义测试套件
?
首先定义多个测试类,可以在同一个包中,也可以在不同包中。
?
右击
Java
项目
→TestNg→Convert to TestNG
?
为
Suite
命名
?
为测试命名
?
Class selection
?
Classes
?
使用类名表示一个测试
?
包中的类可以写成“包名
.
类名”
?
Parallel mode
?
tests
?
表示并行运行测试
?
Thread count
?
用于设置并行线程数量
?
必须与
Parallel mode
同时使用
?
一般设置为并行测试的数量
6.4
修正
testng.xml
<suite thread-count="2" name="
测试套件名称
" annotations="JDK" parallel="tests">
<test name="
自定义测试名
1">
<classes>
<class name="
测试类
1
的名字
"/>
</classes>
</test>
<test name="
自定义测试名
2">
<classes>
<class name="
测试类
2
的名字
"/>
</classes>
</test>
</suite>
?
首先定义多个测试类,可以自同一个包中,也可以在不同包中。
?
annotations
表示注解
?
不能省略
?
JDK
必须大写
6.5
运行测试套件
打开
testng.xml
→点击工具栏运行按钮→选择“
TestNG Suite
”。
7
为测试方法传递参数
7.1
定义参数
?
修改
testng.xml
<suite>
<parameter name="
参数名
" value="
参数值
" />
<test name="
测试名
">
<parameter name="
参数名
" value="
参数值
" />
<classes>
<class name="
测试类的名字
"/>
</classes>
</test>
</suite>
?
在
Suite
范围内定义某个参数的值,对所有的
Test
都有效。
?
在
Test
范围内定义某个参数的值,只是针对该
Test
有效。
?
如果同时在
Suite
和
Test
中定义某个参数,
Test
范围的值会屏蔽
Suite
的值。
?
这种方式的参数值必须直接指定,不如
DataProvider
灵活。
7.2
引用参数
@Test
@Parameters({ "
参数名
1", "
参数名
2" })
public void
测试方法
(String
参数名
1){
}
四、 执行
Excel
测试用例
1
准备
Excel
测试用例
至少包括输入数据和预期结果。
2
读
Excel
?
首先导入
jxl.jar
包
?
读
Excel
的代码
InputStream file=new FileInputStream("e:\\cases.xls");
//
创建输入流
Workbook excel=Workbook.getWorkbook(file);
//
获取
Excel
文件对象
Sheet sheet=excel.getSheet(0);
//
获取文件的指定工作表,默认第一个
List<String[]> list=new ArrayList<String[]>();
Cell cell=null;
//
单元格
for (int i=0; i<sheet.getRows();i++) { //0
表示第
1
行
String[] rowi_col=new String[sheet.getColumns()]; //
创建数组存储一行的多列值
for (int j= 0;j<sheet.getColumns();j++) {
cell=sheet.getCell(j,i);
//
获取第
i
行第
j
列的值
rowi_col[j]=cell.getContents();
}
list.add(rowi_col);//
把刚获取的列存入
list
}
file.close();
3
返回
Object
对象作为参数
Object[][] data=new Object[list.size()][];
for(int i=0;i<list.size();i++)
data[i]=list.get(i);
4
断言失败时输出实际结果
?
断言失败时,断言语句后面的语句将停止运行。
String result=driver.findElement(By.tagName("body")).getText();
Boolean loginResult=result.contains(expect);
try{
Assert.assertTrue(loginResult);
}catch(AssertionError e){
Assert.fail(result);
}
五、 访问数据库
1
导入相关数据库的包
?
网上下载对应数据库的
.jar
包
?
Eclipse
中的操作
?
JAR
系统库→构建路径→配置构建路径→添加外部
JAR
2
加载数据库驱动包
?
Class.forName("
数据库驱动类名
");
?
返回类,作用是加载要连接的数据库的驱动到
JVM
(
Java
虚拟机)。
?
成功加载后,会将
Driver
类的实例注册到
DriverManager
类中。
?
mysql
数据库驱动类名
?
com.mysql.jdbc.Driver
3
连接数据库
?
Connection conn=DriverManager.getConnection ("jdbc:DBMS
类型
://
地址
:
端口号
/
数据库名
", "
用户名
", "
密码
");
?
Connection
是一个接口;
?
DriverManager
是一个类,用于通过驱动程序连接数据库;
?
getConnection
方法返回
Connection
接口。
4
执行
SQL
语句
?
数据查询
?
预编译形式的
SQL
语句
?
数据库可以把编译后的执行代码缓存在内存中,当再次执行相同的
SQL
语句时就不需要重新编译了,只要将参数直接传入编译过的语句执行代码
中就会得到执行。
?
预编译可以提高数据库性能,还可以防范
SQL
注入。
?
String sql="select ... where
列名
1=? and
列名
2=?";
?
?
是固定写法
?
PreparedStatement state=conn.prepareStatement(sql);
?
state.setString(1
,值
1);
?
1
表示第
1
个参数,值是参数值,依次类推
?
获得结果集
?
ResultSet rs=state.executeQuery( );
?
只能执行查询操作,返回一个结果集,不能用于增删改。
?
ResultSet
是一个接口,用于暂存结果集,但无法获得行数。
?
移动行指针,读取一行数据
?
rs.next( )
?
指针移至下一行,成功返回
true
,初始位置在第一行之前。
?
获得列值
?
rs.getString(
列号或列名
)/getDate/getByte/getShort/getInt/getLong/getFloat
?
列号从
1
开始
?
列名用双引号括起来
?
使用此句之前需先用
rs.next( )
读取一行
?
结果集已耗尽
?
原因是
rs
中没有数据
?
增删改
?
int n=state.executeUpdate(String sql)
?
执行增删改操作,返回影响的行数。
5
批量插入多行数据
for( ){
state.setString(1,...);
state.setString(2,...);
...
state.addBatch();
}
state.executeBatch();
6
关闭数据库连接
?
rs.close( );
?
state.close( );