首页 > 技术文章 > 第五章 测试用例规划及TestNG的使用

eagleking0318 2015-06-16 16:50 原文

------Web自动化测试之Webdriver+TestNG--从零到熟练(系列)

经过上一章的学习,我们已经可以将一个手要测试用例转化成代码编写的测试用例,但是一个测试任务通常有很多测试用例。我们就这样一个一个地编写下去吗?答案是否定的。原因是这样编写的测试用例,代码重用率低,数据和测试用例混在一起不便于维护,所以我们得在编写大型的测试用例之前,对我们的测试用例工程进行规划一下。

5.1 测试工程规划

为了方便测试用例的管理,代码重用,以及出错后维护等工作,我们在编写测试用例之前都需要对测试工程的架构进行规划。本人习惯于测试通用函数,测试用例,测试数据分开这样的规划方式,所以我们仍然这样规划我们的测试用例。

(1)创建package:/src/CommonFunction:用来存放所有的公共操作,测试操作步骤,数据操作等。

(2)创建package:/src/TestCases:用来存放具体的测试用例文件,通常一个测试用例文件包含两到三个测试用例,太多的话就不方便调试和出错定位。

(3)创建Package/src/TestData:存放测试用例用到的测试数据文件xml,通常xml文件和测试用例文件同名,这样方便一一对应。

(4)由于TestNG的用例管理是通过testng.xml文件进行的,所以我们不需要再创建测试用例管理相关的文件夹。

通过上面的规划,我们只要在编写测试用例的过程中,编写好公用函数,然后在测试用例文件中调用公用函数,读取测试用例文件进行具体的测试用例操作即可。如果页面有所变动,我们只需要修改相应的文件即可,不用大面积地去修改。

5.2 TestNG DataProvider讲解

 TestNG DataProviderTestNG的数据驱动者,我们要实现数据驱动型的自动化测试用例编写,就一定要学会TestNG DataProvider的使用,网上有很多相关的讲解,如:http://www.cnblogs.com/zhangfei/archive/2012/10/09/2717043.html

而我们的使用方法是什么呢?

(1)读取TestData文件夹下的测试数据文件,如:HelloWorld.xml.

(2)根据节点来读取数据。

这就要求我们实现下面的功能,读取xml文件,及访问节点数据。

一,创建DataProvide.java文件来提供DataProvider

//DataProvide.java

package CommonFunction;

import java.io.File;

import java.lang.reflect.Method;

import org.testng.annotations.DataProvider;

import org.w3c.dom.*; 

import javax.xml.parsers.DocumentBuilder;

import javax.xml.parsers.DocumentBuilderFactory; 

public class DataProvide {      

    public Document doc;

    public void init(String filename) throws Exception

    {

       File inputXml = new File(new File(filename).getAbsolutePath());

       // documentBuilder为抽象不能直接实例化(XML文件转换为DOM文件)

       DocumentBuilder db = null;

       DocumentBuilderFactory dbf = null;

       try {

           // 返回documentBuilderFactory对象

           dbf = DocumentBuilderFactory.newInstance();

           // 返回db对象用documentBuilderFatory对象获得返回documentBuildr对象

           db = dbf.newDocumentBuilder();

           // 得到一个DOM并返回给document对象

           doc = (Document)db.parse(inputXml);

         }

         catch (Exception e) {

              e.printStackTrace();

          }

        

    }   

    @DataProvider(name="Test_xml_dataprovider")

    public Object[][] providerMethod(Method method){

       return new Object[][]{new Object[]{doc}};

    }

}

读取传递过来的Xml文件,并传递Document对象给调用的函数。这也是java操作xml文件的基础,如果不会,请自己学习。@DataProvider(name=””)用来标识不同的DataProvider,以便后续对其进行调用。

二,读取具体的节点值

通过传递过来的Document对象,父节点和节点标签,读取节点对应的值。

#DataReader.java

package CommonFunction; 

import org.w3c.dom.*; 

public class DataReader {   

    public String readnodevalue(Document doc,String firsttag,String secondtag)

    {

       String result="";

       Element root=doc.getDocumentElement();

       NodeList childnode = root.getElementsByTagName_r(firsttag);

       NodeList resnode=childnode.item(0).getChildNodes();

       for(int i=0;i<</SPAN>resnode.getLength();i++)

       {

           if(resnode.item(i).getNodeName().equals(secondtag))

           {

              result=resnode.item(i).getTextContent();

              break;

           }

       }         

       return result;          

    }   

}

通过这两个文件,我们就可以读取xml文件为测试用例文件提供测试数据。

5.3 新架构下测试用例编写

在上我们还以第四章的众筹网登录检测为例,来在新框架进编写对应的测试用例。具体步骤如下:

1)分析测试用例。我们要实现测试用例和测试数据的分离和代码重用。就要先将上面的两个DataProvider相关的文件存放到package:/src/CommonFunction下。然后再创建一个共用函数类文件CommonFunctions.java文件,存放公用的函数。

2)编写公用函数。创建java类文件CommonFunctions.java,由于登录退出操作是通用的操作,所以我们会在这个类文件中写两个相应的函数。并且根据我们第四章的了解,在这个测试用例中我们用到了获取元素文本的操作,所以我们也写一个函数来完成这个功能。最终的CommonFunctions.java代码如下:

package CommonFunction;

import java.util.HashSet;

import java.util.Set; 

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver; 

import org.openqa.selenium.support.ui.Select;

import org.openqa.selenium.WebElement; 

import static org.junit.Assert.*; 

public class CommonFunctions {  

       static public WebDriver driver;      

       public CommonFunctions()

       {

              //do nothing

       }

       public CommonFunctions(String url)

       {

           // 如果你的 FireFox 没有安装在默认目录,那么必须在程序中设置 

              //System.setProperty("webdriver.firefox.bin", "E:\\software\\firefox\\firefox.exe"); 

           // 创建一个 FireFox 的浏览器实例 

              driver = new FirefoxDriver();

              driver.manage().window().maximize();

              driver.get(url);

             

       }       

       public void geturl(String url) throws Exception

       {

              driver.get(url);

              Thread.sleep(5000);

       }   

       public void teardown()

       {           

              try {

                     driver.quit();                

              } catch (Exception e) {       

                     e.printStackTrace();

              }

       }

      public String gettext(String type,String location)

       {

              WebElement elem=null;

              if(type.equals("xpath"))

              {

                     elem=driver.findElement(By.xpath(location));                  

              }

              if(type.equals("name"))

              {

                 elem=driver.findElement(By.name(location));                    

              }

              if(type.equals("id"))

              {

                     elem=driver.findElement(By.id(location));                

              }

              if(type.equals("classname"))

              {

                     elem=driver.findElement(By.className(location));                  

              }

              if(type.equals("css"))

              {

                     elem=driver.findElement(By.cssSelector(location));                 

              }

              return elem.getText();

       }

      public void clickitem(String type,String location)

       {

              WebElement elem=null;

              if(type.equals("xpath"))

              {

                     elem=driver.findElement(By.xpath(location));                  

              }

              if(type.equals("classname"))

              {

                     elem=driver.findElement(By.className(location));                  

              }

              if(type.equals("text"))

              {

                     elem=driver.findElement(By.linkText(location));              

              }

              if(type.equals("name"))

              {

                     elem=driver.findElement(By.name(location));                  

              }

              elem.click();

       }

      public void clearvalue(String type,String location)

       {

              WebElement elem=null;

              if(type.equals("xpath"))

              {

                     elem=driver.findElement(By.xpath(location));

              }

              if(type.equals("name"))

              {

                 elem=driver.findElement(By.name(location));

              }

              elem.clear();

       }

      public void inputvalue(String type,String location,String text)

       {

              WebElement elem=null;

              if(type.equals("xpath"))

              {

                     elem=driver.findElement(By.xpath(location));           

              }

              if(type.equals("name"))

              {

                     elem=driver.findElement(By.name(location));                  

              }

              if(type.equals("id"))

              {

                     elem=driver.findElement(By.id(location));         

              }

              elem.sendKeys(text);           

       }

       public void checkequal(String str1,String str2)

       {

              assertEquals(str1,str2);

       }

public void login(String name,String psd) throws Exception

       {

           this.clickitem("classname", "Js-showLogin");

           Thread.sleep(3000);

           this.clearvalue("name", "username");

           this.inputvalue("name", "username", name);

           this.clearvalue("name", "user_pwd");

           this.inputvalue("name", "user_pwd", psd);

              Thread.sleep(1000);

              this.clickitem("classname", "zc");

              Thread.sleep(5000);

       }  

       public void logout() throws Exception

       {

              this.geturl("http://www.zhongchou.cn/usernew-loginout");

       }

      

}

代码讲解:

Ø  首先我们对具体的操作,如click,clear,input等进行了相应的封装,函数中我们已加上了大量的注释,不难理解。

Ø  编写登录函数login(),退出函数logout(),获取元素文本函数gettext()等。

Ø  这些儿函数就像我们小时候玩的积木,下面我们就可以利用这些儿函数来组织我们的测试用例。

(3)准备测试数据。在package:/src/TestData下创建xml文件LoginTest.xml(对应的我们也会创建测试用例文件LoginTest.java),在文件中存放我们需要用到的测试数据。

内容如下:

第五章 <wbr>测试用例规划及TestNG的使用

 

(4)编写测试用例。下面我们编写对应的测试用例文件,在package:/src/TestCases下创建测试用例文件LoginTest.java,在文件中编写具体的测试步骤。

具体代码如下:

package TestCases;

import org.w3c.dom.*;

import org.testng.annotations.Test;

import org.testng.annotations.BeforeTest;

import org.testng.annotations.AfterTest; 

import CommonFunction.CommonFunctions;

import CommonFunction.DataProvide;

import CommonFunction.DataReader;

 

public class LoginTest extends DataProvide{

  public CommonFunctions comfun;

  public DataReader dr; 

  @BeforeTest

  public void setup() throws Exception{

     String url = "http://www.zhongchou.cn";

     comfun=new CommonFunctions(url);

     dr=new DataReader();       

     //设置数据源

     init("src/TestData/LoginTest.xml");

  } 

  @Test(dataProvider="Test_xml_dataprovider")

  public void testlogin(Document params) throws Exception {

   

comfun.login(dr.readnodevalue(params, "login", "username"), dr.readnodevalue(params, "login", "password"));

  comfun.checkequal(comfun.gettext("css", dr.readnodevalue(params, "login", "checkpoint1")), dr.readnodevalue(params, "login", "value1"));

  //退出登录

  comfun.logout();

 

  }

  @AfterTest

  public void teardown() {

  comfun.teardown();

  }

}

(5)运行测试用例。此时运行一下这个测试用例文件,可以看到和第四章的一样结果。而测试函数中的代码其实就三行,而且在其他的测试用例中也可以去调用登录和退出函数,从而实现了代码重用。如果网站有变化,我们只需要修改一下测试用例数据文件LoginTest.xml,简化了用例维护工作。

(6)测试用例的添加。当我们把测试用例工程规划完成后,以后要做的工作就是在公用类中添加公用函数,添加具体的测试用例文件及对应的测试数据。

5.4 测试用例的转化

    到目前为止,我们已经可以在漂亮的架构下编写具体的自动化测试用例了。可是还有一个问题影响着我们自动化测试用例的实施,如何将手工测试用例转化成自动化测试用例?

(1)用例的收集。自动化要测试的必须是成熟的功能,完成日常的回归测试。在一个比较重视质量的公司里,测试用例的收集是必须要做的工作,如果有相应的手工测试用例,转化成自动化测试用例就比较容易。如果没有对应的手工测试用例,我们需要补充手工测试用例,不能在没用用例的情况下,直接编写自动化测试用例,这样容易漏测试。

(2)用例评审。并不是所有的手工测试用例都能转化成自动化测试用例的,而且也没有必要。自动化测试主要的工作就是回归测试,一个回归测试你不可能将方方面面都测试一遍,保证主功能没有问题就可以了。所以在编写自动化测试用例之前,评审一下测试用例,哪些儿需要自动化,哪些儿不需要。

(3)从用例中抽出公用函数。在评审完测试用例后,我们就可以抽出哪些儿操作是所有测试用例都用到的,哪些儿是一部分测试用例中用到的,哪些儿是个别用到的?对这些儿有了一定的概念,就可以编写相应的公用函数。

(4)灵活运用各种检测技巧。计算机不能像人工测试那样灵活,只能通过代码进行对比,所以要灵活设置各种检测点儿,或是灵活执行操作。比如:如果我们要退出网站,人工是点击退出按钮。可是在退出按钮不容易定位的时候,我们可以直接打开退出对应的链接即可。

5.5 本章小结

    本章我们讲解了如何规划一个测试用例工程,TestNG DataProvider数据驱动的使用。通过合理的规划测试代码结构,利用TestNG的数据驱动功能,我们可以实现测试用例和测试数据的分离,实现代码重用,使用测试用例的维护变的更加容易。接着又讲到了如何从手工测试用例转化成自动化测试用例,这是我们编写自动化测试用例的基本功。接下来我们将深入讲解如何使用testng.xml文件组织测试用例,以及测试报告的生成。

 

推荐阅读