testng的功能很强大,利用@dataprovider可以做数据驱动,数据源文件可以是excel,xml,yaml,甚至可以是txt文本。
@dataprovider注解简介:
@dataprovider标记专门为测试方法提供参数的方法。这类方法必须返回object[ ][ ]类型的二维数组或者iteratora87fdacec66f0909fc0757c19f2d2b1d[],每一行的object[],都是测试方法的一个测试数据集,测试方法会为每个测试数据集执行一次。如果没有指定参数的名称,则默认为方法的名称,方法的名称没有限制。
@dataprovider的小例子:
import java.lang.reflect.method;import org.testng.annotations.dataprovider;import org.testng.annotations.test;public class test { @dataprovider(name = "user") public object[][] createuser(method m) { system.out.println(m.getname()); return new object[][] { { "root", "root" }, { "test", "root" }, { "test", "test" } }; } @test(dataprovider = "user") public void verifyuser(string username, string password) { system.out.println("verify user : " + username + ":" + password); assert username.equals(password); }}
如上所示@dataprovider注解了createuser方法,返回的二位数组里有三行数据,每行两列。
所以@test(dataprovider = "user")注解的verifyuser方法有两个参数,用来接收每一行的两个数据,如果createuser返回的数据数组的列数和verifyuser的参数个数不同就会报错的。
因为返回的有三行,所以verifyuser会被执行三次。结果如下:
passed: verifyuser("root", "root")failed: verifyuser("test", "root")passed: verifyuser("test", "test")
csv文件数据读取和@dataprovider
我自己做了一个以csv为例的测试架子,部分代码可通用。
csv文件读取类(可通用,目录自己可以修改,也可改变成读取excel、txt等文件):
import java.io.bufferedreader;import java.io.file;import java.io.filereader;import java.util.arraylist;import java.util.iterator;import java.util.list;import java.util.map;import java.util.treemap;import java.util.regex.matcher;public class csvdata implements iterator<object[]> { private bufferedreader br = null; //行数 private int rownum = 0; //获取次数 private int currowno = 0; //列数 private int columnnum = 0; //key名 private string[] columnname; //csv中所有行数据 private list<string> csvlist; //实际想要的行数据 private list<string> csvlistneed; /* * 在testng中由@dataprovider(dataprovider = "name")修饰的方法 * 取csv时,调用此类构造方法(此方法会得到列名并将当前行移到下以后)执行后,转发哦 * testng自己的方法中去,然后由它们调用此类实现的hasnext()、next()方法 * 得到一行数据,然后返回给由@test(dataprovider = "name")修饰的方法,如此 * 反复到数据读完为止 * * * @param filepath csv文件名 * @param casename 用例名 */ public csvdata(string filename, string caseid) { try { file directory = new file("."); string ss = "resources."; file csv = new file(directory.getcanonicalfile() + "\\src\\test\\" + ss.replaceall("\\.", matcher.quotereplacement("\\")) + filename + ".csv"); br = new bufferedreader(new filereader(csv)); csvlist = new arraylist<string>(); while (br.ready()) { csvlist.add(br.readline()); this.rownum++; } string stringvalue[] = csvlist.get(0).split(","); this.columnnum = stringvalue.length; columnname = new string[stringvalue.length]; for (int i = 0; i < stringvalue.length; i++) { columnname[i] = stringvalue[i].tostring(); } this.currowno++; csvlistneed = new arraylist<string>(); for (int i = 1; i < rownum; i++) { string values[] = csvlist.get(i).split(","); if (caseid.equals(values[0])) { csvlistneed.add(csvlist.get(i)); } } this.rownum = 2;//就取一行 } catch (exception e) { e.printstacktrace(); } } @override public boolean hasnext() { if (this.rownum == 0 || this.currowno >= this.rownum) { try { br.close(); } catch (exception e) { e.printstacktrace(); } return false; } else { return true; } } @override public object[] next() { /* * 将数据放入map */ map<string, string> s = new treemap<string, string>(); string csvcell[] = csvlistneed.get(0).split(","); for (int i = 0; i < this.columnnum; i++) { string temp = ""; try { temp = csvcell[i].tostring(); } catch (arrayindexoutofboundsexception ex) { temp = ""; } s.put(this.columnname[i], temp); } object r[] = new object[1]; r[0] = s; this.currowno++; return r; } @override public void remove() { throw new unsupportedoperationexception("remove unsupported"); }}
这个类实现了iterator<object[]>迭代器,testng调用此类实现的hasnext()、next()方法得到一行数据,在next()方法中可以看到,我把数据是放在map<string, string>中的,再把map放在object[]里,所以测试方法的参数就必须是一个map<string, string>。我这里改成了只读取一行,因为一个csv文件的一个caseid只应该有一行。
数据驱动类:
import java.lang.reflect.method;import java.util.iterator;import org.testng.annotations.dataprovider;public class dataprovidertest { /** * @dataprovider的返回值类型只能是object[][]与iterator<object>[] * * @param method * @return */ @dataprovider public iterator<object[]> datasource(method method) { return (iterator<object[]>) new csvdata(method.getdeclaringclass().getsimplename(), method.getname()); }}
method方法是通过反射获取的,总之哪个方法调用我method就是那个方法。
method.getdeclaringclass().getsimplename()可以获取方法所属的类的类名。
我这里规定了csv的文件名就是测试类的类名,用例名就是方法名。
return (iterator<object[]>) new csvdata(…)就是将csv读取类读取的结果返回,返回的类型是iterator<object[]>的,符合@dataprovider的返回值类型要求。当@test(dataprovider = "datasource")注解的测试方法执行时就会调用iterator的hasnext()判断是否有数据和next()获取数据。
测试类:
import java.util.map;import org.testng.annotations.test;public class datatest extends dataprovidertest { @test(dataprovider = "datasource") public void id2(map<string, string> data) { system.out.println(data); } @test(dataprovider = "datasource") public void id1(map<string, string> data) { system.out.println(data); }}
输出结果如下:
passed: id1({caseid=id1, flag=y, property=flowmodel, type=com.mybank.bkloanapply.core.model.basemodel, value=basemodel.csv@1})passed: id2({caseid=id2, flag=m, property=context, type=java.util.map, value=a:object.csv@1})
总结
通过以上例子可以看到,无论@dataprovider注解的方法返回的是object[ ][ ]还是iteratora87fdacec66f0909fc0757c19f2d2b1d[],最后测试方法获得的参数都是object[ ]里放的东西,第一个例子里放了两列string,第二个例子里放了map05ad6303f369fc4ccec4412db2772d19,所以第一个测试类的测试方法的参数是两个string,第二个测试类的测试方法的参数是map05ad6303f369fc4ccec4412db2772d19,必须保持一致才行。
以上就是testng数据驱动简介的详细内容。
