TestNG允许我们在Test Method 以及 @Before/After类注解 、 @Factory类注解注解的方法中传入参数
参数传递的方式有两种,一种是借助xml文件配置,一种是以编码的方式传递参数。
一、借助xml文件配置:1.在方法上使用@Parameters({"paraname"})注解,然后在套件xml文件中声明变量即可使用。方法有多少个参数,就需要在@Parameters内声明多少个变量,两者参数顺序对应。另外Test提供了@Optional注解,可以为参数提供默认值,如果在xml文件没有找到这个变量,则会使用@Optional的默认值。
示例:新建一个TestNG类和对应的xml配置文件。
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
public class ParameterExample {
@Parameters({"xxx","yyy","varNotFound"})
@Test
public void parameterExample(String p1, String p2, @Optional("this is default value") String p3 ){
System.out.println(p1+" "+p2);
System.out.println(p3);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="testSuite">
<parameter name="yyy" value="hello yyy"/>
<parameter name="xxx" value="hello xxx"/>
<test name="test1">
<classes>
<class name ="com.testng.ParameterExample" />
</classes>
</test>
</suite>
上面的例子,声明了三个变量,参数名依次为xxx, yyy, varNotFound。在xml文件中分别定义了xxx和yyy变量,varNotFound没有在xml文件中定义,因此会使用@Optional注解的默认值。上面代码的输出结果如下:
我们要注意parameter参数在xml文件中声明的位置,因为这些参数也是有作用域的,这些参数的声明既可以放在套件suite下也可以放在用例test下。对于在suite和test中都声明了的同名变量,在Test内的变量会覆盖掉Suite的变量。这个很好理解,类比java,局部变量在其作用域内也会覆盖掉全局变量。比如下面的代码,在xml文件中,同时在suite域和test域声明了变量sameNameVar,当我们运行这个Test的时候,会使用这个Test内部的sameNameVar变量而不是来自Suite的sameNameVar。
package com.testng;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
public class TestParaScope {
@Parameters({"sameNameVar"})
@Test
public void parameterExample(String p){
System.out.println(p);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="testSuite">
<parameter name="sameNameVar" value="定义在Suite的变量被使用了"/>
<test name="test1">
<parameter name="sameNameVar" value="定义在Test的变量被使用了"/>
<classes>
<class name ="com.testng.TestParaScope" />
</classes>
</test>
</suite>
运行结果;
在xml文件中配置参数的方式,比较适合传递一些简单参数。借助编码方式,我们可以往Test Method里传递复杂的对象。编码传参,需要使用@DataProvider注解,被@DataProvider注解的方法,只能返回一个Object二维数组或一个Iterator<Object[]>来提供复杂的参数对象。我们先讲返回Object[][]二维数组的情况。Object[][]这个二维数组的每一个一维数组代表一个对象。这个一维数组中的元素类型,个数必须和使用这个provider的函数的参数匹配。使用这个Provider的Method会遍历Provider传递过来的每一个对象,每个对象都执行一次方法体内的代码。
我们结合代码来理解
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class TestDataProvider {
@DataProvider(name = "myDataProvider")
public Object[][] myDataProvider(){
/*返回一个二维数组*/
return new Object[][]{
/*二维数组下的每一个一维数组*/
{ "Apple", new Integer(20)},
{ "Banana", new Integer(11)}
};
}
@Test(dataProvider = "myDataProvider")
public void testDataProvider(String fruitName, Integer fruitNum){
System.out.println("我从myDataProvider仓库拿到了" + fruitNum + "个" + fruitName);
}
}
这段代码中的DataProvider提供了一个二维数组,这个二维数组下的每一个一维数组,都是一个水果对象,这个水果对象的属性有两个,分别是水果名称和水果价格,水果名称是String类型,水果价格是Integer类型。使用了这个DataProvider的Method的参数列表有两个参数,对应了水果对象的两个属性,这两个参数类型也和水果的两个属性的参数类型相对应。TestMethod对DataProvider传递过来的每个对象,都会执行依次方法体内的代码,因此我们会得到两行输出。
在拥有大量有规律的测试数据的时候,我们可不想以敲出一个个一维数组初始化为二维数组的形式来初始化测试数据。这时候我们可以使用返回类型为Iterator<Object[]>的DataProvider,有同学可能就会困惑了,如果DataProvider返回Iterator<Object[]>那Test Method要怎么写才能收下这些数据呢。Iterator<Object[]>和Object[][]的区别就在于,Iterator充当了二维数组的第二维,实际上就是遍历的方式变了,核心的东西还是它里面的一维数组,所以我们前面说的一维数组的元素个数,元素类型必须和使用的Test Method的参数列表一致的原则还是适用的。所以不管返回类型是啥,Test Method该咋写就咋写。下全面我给出了两个返回Iterator<Object[]>的例子,可以看下TestMethod的参数列表是如何写的。
package com.testng;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class TestDataProvider {
/*水果对象类*/
class Fruit{
Fruit(String f, Integer n){
fruitName = f;
fruitNum = n;
}
private String fruitName;
private int fruitNum;
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
public int getFruitNum() {
return fruitNum;
}
public void setFruitNum(int fruitNum) {
this.fruitNum = fruitNum;
}
}
@DataProvider(name = "iteratorDataProvider")
public Iterator<Object[]> iteratorDataProvider(){
List<Object[]> objList = new ArrayList<Object[]>();
for(int i = 1; i < 5; i++){
/*我这里一维数组元素个数是2,第一个元素是String,第二个元素是Integer,所以TestMethod的参数有两个,一个是String,一个是Integer*/
objList.add(new Object[]{"第" + i + "名得分", new Integer(i) });
}
return objList.iterator();
}
@Test(dataProvider = "iteratorDataProvider")
public void testDataProvider(String str, Integer i){
System.out.println(str+i);
}
@DataProvider(name = "myDataProvider")
public Iterator<Object[]> myDataProvider(){
List<Fruit> fList = new ArrayList<Fruit>();
fList.add(new Fruit("Apple", new Integer(123)));
fList.add(new Fruit("Banana", new Integer(456)));
List<Object[]> fArrList = new ArrayList<Object[]>();
for (Object f : fList) {
/**
* 注意我这里是new一个元素为Fruit的数组,前面我们说了,一维数组的元素类型,元素个数要和使用的函数参数列表一致,
* 那么下面的TestMethod的参数列表就应该只有一个元素,且元素类型为Fruit
* */
fArrList.add(new Object[] { f });
}
return fArrList.iterator();
}
@Test(dataProvider = "myDataProvider")
public void testDataProviderObject(Fruit f){
System.out.println("我从myDataProvider仓库拿到了" + f.getFruitName() + "个" + f.getFruitNum());
}
}
还有一点提一下,如果你希望别的类声明DataProvider方法,然后在另一个TestNG类内使用它,那么这个DataProvider方法必须被声明为静态方法,且在使用的时候,需要声明它所属的类。
public class StaticProvider {
@DataProvider(name = "create")
public static Object[][] createData() {
return new Object[][] {
new Object[] { new Integer(42) }
};
}
}
public class MyTest {
@Test(dataProvider = "create", dataProviderClass = StaticProvider.class)
public void test(Integer n) {
// ...
}
}
那么,TestNG中给方法传递参数的方式介绍就到此为止辣。附上TestNG官官方文档,你想查的都在这里:
https://testng.org/doc/documentation-main.html#parameters