51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 1216|回复: 1
打印 上一主题 下一主题

数据驱动剖析及 TestNg 数据驱动介绍

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-3-20 13:23:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
数据驱动特点

数据驱动是指在脚本固定的情况下,根据数据的条数来决定脚本的运行次数,即有几组数据,脚本就会
运行几遍。

数据驱动概述

数据驱动(Data Driven),这里强调的是数据,驱动即执行,那么数据驱动就是根据数据来执行测试脚本。

场景:测试登录,分别用刘能和赵四的帐号去测试。
先写一个公共方法来描述登录的过程:(伪代码实现)
  1. public boolean login(String username, String password){
  2.     //do login
  3. }
  4. 再到测试方法里面去调用这个login方法/函数:
  5. public void test1(){
  6.     login("liuneng","123456");
  7. }

  8. public void test2(){
  9.     login("zhaosi","654321");
  10. }
复制代码

这样测试用例就写完了,执行test1与test2两个方法即可。但细心的你可能会发现test1与test2这两个测试方
法里的方法体除了数据,其它完全一样,这就存在重构的空间了:
  1. public boolean login(String[][] accounts){
  2.     for(int i = 0; i<accounts.length; i++){
  3.         //do login
  4.     }
  5. }

  6. public void test(){
  7.     String[][] accounts = [["liuneng","123456"],["zhaosi","654321"]];
  8.     login(accounts);
  9. }
复制代码

经过重构后的代码,就有点数据驱动的意思了,根据accounts的length来决定login方法/函数体运行几次,这
样维护起来就方便了,假如又有一个老王的帐号想用来测试,就不需要再加一个测试方法了,只需要:
String[][] accounts = [["liuneng","123456"],["zhaosi","654321"],["laowang","000000"]];
重构后的代码,是不是令你很激动?原来这就是数据驱动!别急,淡定,这还不是真的数据驱动,因为上面
只有一个测试方法,最后执行完后,报告中记录的也是只有一个测试方法,而场景中:分别用刘能和赵四的
帐号去测试,是希望在测试报告中有两个测试方法出现,显然上面的代码还不能满足我们的需求,于是进一
步优化:
  1. public boolean login(String username, String password){
  2.     //do login
  3. }  

  4. public void test(String username, String password){
  5.     login(username,password);
  6. }

  7. public void executor(){
  8.     String[][] accounts = [["liuneng","123456"],["zhaosi","654321"]];
  9.     for(int i = 0; i<accounts.length; i++){
  10.         test(accounts[i][0],accounts[i][1]);
  11.     }
  12. }
复制代码

是的,离数据驱动原理真相越来越近了,上面多了个executor方法,这是啥?这就是车子的发动机引擎啊,
就是测试脚本的执行引擎,让测试方法能够被执行起来,以及根据你所提供的测试数据的条数,决定测试
方法的执行次数,并且报告中会显示是两个测试方法,这就是数据驱动。测试框架就是一个执行引擎,并
且测试框架都会支持数据驱动这一基本诉求,比如TestNg里的dataProvider,junit里的Parameters等。下
面将为大家介绍TestNg里的dataProvider的用法。
TestNg数据驱动

TestNg的数据驱动也是以注解的形式来表达的:

  1. public class TestData {

  2.     @DataProvider(name="dataDemo")
  3.     public Object[][] dataProvider(){
  4.         return new Object[][]{{1,2},{3,4}};
  5.     }

  6.     @Test(dataProvider="dataDemo")
  7.     public void testDemo(int a, int b){
  8.         int sum = a + b;
  9.         System.out.println("this is sum: "+sum);
  10.     }

  11. }
复制代码

说明:

TestNg的数据驱动的提供数据的方法用@DataProvider注解
@DataProvider中的name属性表示该数据源的名称
@DataProvider的方法要返回一个Object[][]的二维数组,当然也可以是Iterator的数据结构。
在测试方法中要用到数据源,则要在@Test中加上dataProvider属性,其值为数据源的名称。
Object[][]二维数组{{1,2},{3,4}}可以理解为有两组数据,分别为:{1,2},{3,4},所以测试方法会运行两
次,每一次运行时,测试方法的第一个参数a对应这一组数据中的第一个,也就是1或者3,第二个参数b则
对应这一组数据中的第二个,也就是2或者4,如果还有数据,则依次类推。当然数据源的每一组数据的个
数要大于或等于测试方法的参数个数,否则会报错,且数据类型要对应上,否则也会报错。
上面的示例中我们指定了@DataProvider数据源的名称为dataDemo,其实也可以不指定其名称,如果不指
定其名称,则数据源的名称为该数据源方法的方法名,比如:

  1. public class TestData {

  2.     @DataProvider
  3.     public Object[][] dataProvider(){
  4.         return new Object[][]{{1,2},{3,4}};
  5.     }

  6.     @Test(dataProvider="dataProvider")
  7.     public void testDemo(int a, int b){
  8.         int sum = a + b;
  9.         System.out.println("this is sum: "+sum);
  10.     }

  11. }
复制代码

上面的例子中,@DataProvider数据源的方法与测试方法是在同一个测试类里,或者@DataProvider数据源的
方法放在测试类的父类中。但其实还有一种方式,@DataProvider数据源的方法可以单独的放在一个类里,比如:

  1. public class DataSource {

  2.     @DataProvider
  3.     public static Object[][] dataProvider(){
  4.         return new Object[][]{{1,2},{3,4}};
  5.     }

  6. }

  7. public class TestData {

  8.     @Test(dataProvider="dataProvider",dataProviderClass=DataSource.class)
  9.     public void testDemo(int a, int b){
  10.         int sum = a + b;
  11.         System.out.println("this is sum: "+sum);
  12.     }

  13. }
复制代码

说明:

在测试方法中可以指定一个数据源的类,用dataProviderClass来指定
如果用了dataProviderClass指定数据源的类,则@DataProvider数据源的方法必须是static的。
其实@DataProvider数据源的方法,还可以提供一个参数Method,这个Method是指要使用该数据源的测试
方法的Method对象,比如:

  1. public class TestData {

  2.     @DataProvider
  3.     public Object[][] dataProvider(Method method){
  4.         //method对象指使用该数据源的测试方法的Method对象,这是java中的一种反射
  5.         System.out.println(method.getName());//输出testDemo
  6.         return new Object[][]{{1,2},{3,4}};
  7.     }

  8.     @Test(dataProvider="dataProvider")
  9.     public void testDemo(int a, int b){
  10.         int sum = a + b;
  11.         System.out.println("this is sum: "+sum);
  12.     }

  13. }
  14. 很显然,有了这个Method对象后,就很方便我们扩展了,请看下面的例子:

  15. 先写一个类,将所有的数据都放在里面:
  16. public class DataSource {
  17.     /**
  18.      * dataMap里有两个数据源,分别是testDemo与testDemo1
  19.      * 根据测试方法的名称,来使用不同的数据源。
  20.      * 比如testDemo方法就用数据源{{1,2},{3,4}}
  21.      * testDemo1方法就用数据源{{5,6},{3,4}}
  22.      * @return
  23.      */
  24.     public Map<String, Object[][]> dataSource(){
  25.         Map<String, Object[][]> dataMap = new HashMap<String, Object[][]>();
  26.         Object[][] o1 = new Object[][]{{1,2},{3,4}};
  27.         dataMap.put("testDemo", o1);
  28.         Object[][] o2 = new Object[][]{{5,6},{7,8}};
  29.         dataMap.put("testDemo1", o2);
  30.         return dataMap;
  31.     }

  32. }
  33. 再结合到@DataProvider数据源方法与测试方法中去:
  34. public class TestData {

  35.     @DataProvider
  36.     public Object[][] dataProvider(Method method){
  37.         DataSource data = new DataSource();
  38.         Object[][] obj = data.dataSource().get(method.getName());
  39.         return obj;
  40.     }

  41.     @Test(dataProvider="dataProvider")
  42.     public void testDemo(int a, int b){
  43.         int sum = a + b;
  44.         System.out.println("this is sum: "+sum);
  45.     }

  46.     @Test(dataProvider="dataProvider")
  47.     public void testDemo1(int a, int b){
  48.         int sum = a + b;
  49.         System.out.println("this is sum: "+sum);
  50.     }

  51. }
  52. 以上两个测试方法用到了同一个数据源@DataProvider,这样为我们以后写测试框架定下了基调。

  53. enum的使用

  54. enum是java中的一个关键字,中文翻译过来是枚举,先来看看用法:

  55. public enum TestEnum {
  56.     /**
  57.      * 定义两组数据,分别是{200,"success."}与{400,"failed."}
  58.      * 枚举的意思是根据定义好的数据,来生成对象,有几组数据,就生成几个对象
  59.      */
  60.     SUCCESS(200,"success."),
  61.     FAIL(400,"failed.");

  62.     private int retCode;

  63.     private String retMsg;

  64.     private TestEnum(int retCode, String retMsg) {
  65.         this.retCode = retCode;
  66.         this.retMsg = retMsg;
  67.     }

  68.     public int getRetCode() {
  69.         return retCode;
  70.     }

  71.     public String getRetMsg() {
  72.         return retMsg;
  73.     }

  74.     public static void main(String[] args) {
  75.         System.out.println(TestEnum.SUCCESS.getRetCode());//输出200
  76.         System.out.println(TestEnum.FAIL.getRetMsg());//输出failed.
  77.     }

  78. }
复制代码



分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

该用户从未签到

2#
 楼主| 发表于 2018-3-20 13:23:27 | 只看该作者
说明:枚举的意思是根据定义好的数据,来生成对象,有几组数据,就生成几个对象。上面的例子中生成
了SUCCESS和FAIL两个对象。
联想到之前的数据驱动,是有几组数据测试方法就会执行几次,枚举是有几组数据就会生成几个对象,冥
冥之中枚举与数据驱动好像有那么点联系,既然枚举已经定义好了,那么根据上面我们介绍到的知识点,
把枚举与数据驱动联系起来。

  1. public class DataSource {

  2. public Map<String, Object[][]> dataSource(){
  3. Map<String, Object[][]> dataMap = new HashMap<String, Object[][]>();
  4. TestEnum[] te = TestEnum.values();//enum提供的方法,是把生成的几组对象给放到一个数组里{
  5. TestEnum.SUCCESS,TestEnum.FAIL}
  6. Object[][] o1 = new Object[te.length][];//定义一个数据源,其数据条数与TestEnum的数组条数一样
  7. for (int i = 0; i < o1.length; i++) {
  8. Object[] o1temp = new Object[]{te[i]};//将每一个TestEnum再单独的放到一个数组里,o1temp
  9. 为{TestEnum},
  10. o1[i] = o1temp;//{{TestEnum.SUCCESS},{TestEnum.FAIL}},o1[0]就为{TestEnum.SUCCESS},
  11. 枚举出来的TestEnum.SUCCESS与TestEnum.FAIL都是一个TestEnum对象,只是对象里面的属性值不同
  12. }
  13. //循环添加后,数据结构为{{TestEnum.SUCCESS},{TestEnum.FAIL}}
  14. dataMap.put("testDemo", o1);
  15. Object[][] o2 = new Object[][]{{5,6},{7,8}};
  16. dataMap.put("testDemo1", o2);
  17. return dataMap;
  18. }

  19. }

  20. public class TestData {

  21. @DataProvider
  22. public Object[][] dataProvider(Method method){
  23. DataSource data = new DataSource();
  24. Object[][] obj = data.dataSource().get(method.getName());
  25. return obj;
  26. }

  27. @Test(dataProvider="dataProvider")
  28. public void testDemo(TestEnum te){
  29. System.out.println("retCode is: "+te.getRetCode() +" retMsg is: "+te.getRetMsg());
  30. }

  31. @Test(dataProvider="dataProvider")
  32. public void testDemo1(int a, int b){
  33. int sum = a + b;
  34. System.out.println("this is sum: "+sum);
  35. }

  36. }
复制代码

以上对于对象放于二维数组中不懂的,请去翻阅前面的文档,有对二维数组的详细讲解。

关于@Parameters

这里所说的@Parameters就TestNg的@Parameters,在前面我们介绍过用TestNg的xml配置文件来制定策
略执行测试类,那xml配置文件与测试方法间如何进行参数传递?@Parameters就是干这个事的,所以,
@Parameters只是参数的传递,并不是真正意义上的数据驱动,我个人对这个应用的不多,但还是有必
要给大家介绍一下用法。

  1. public class TestDemo {

  2. @Parameters({"a","b"})
  3. @Test
  4. public void testDemo(int a, int b){
  5. int sum = a+b;
  6. System.out.println("this is sum: "+sum); //输出3
  7. }

  8. }
  9. <?xml version="1.0" encoding="UTF-8"?>
  10. <suite name="Suite" verbose="1" parallel="false" thread-count="1">
  11. <parameter name="a" value="1"/>
  12. <parameter name="b" value="2"/>
  13. <test name="Test1">
  14. <classes>
  15. <class name="com.test.demo.TestDemo" />
  16. </classes>
  17. </test>
  18. </suite>
复制代码

说明:

@Parameters注解能用在测试方法或者@Before/@After/@Factory上面。
@Parameters中的字符串是在xml中的parameter结点的name。
在xml中的parameter结点的value就是该name对应的具体的参数值。
@Parameters注解的{}中的参数的顺序,对应其标注的方法上的参数的顺序。

回复 支持 反对

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-4-24 03:40 , Processed in 0.064777 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表