自動化測試--實現一套徹底解耦的簡單測試框架

selenium中有提供pageObject,支持將頁面元素和動做單獨封裝到一個類中。html

可是,當頁面元素髮生變化的時候(在項目的維護過程當中,很很容易發生的),就須要去修改源代碼。爲了解決這個問題,能夠實現一套徹底解耦的簡單測試框架。java

該框架的主要思想,是 將各個測試頁面的定位信息存放到xml中,解析後的xml信息映射到相應的類中。當頁面定位信息改變的時候,只需修改xml文件便可。web

下面是項目框架:chrome

base:用來存放一些測試初始化操做以及對selenuim部分功能的2次封裝,以後會上代碼。apache

pageLocator:用來存放一個頁面定位器的類,該類包含by、value、desc三個屬性。方便將頁面定位元素的信息映射到咱們這個類上。數組

testCase:測試用例瀏覽器

util:幫助類。存放 如以前博客中封裝的getDriver等app

 

下面是該框架的具體實現:框架

1.先使用maven引入各jar包dom

selenium、TESTNG(管理用例)、log4j(記錄日誌)、dom4j(解析xml)、poi(讀取excel)。下面是筆者的pom.xml

 1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 2   <modelVersion>4.0.0</modelVersion>
 3   <groupId>com.claire.leafly</groupId>
 4   <artifactId>projectLemon</artifactId>
 5   <version>0.0.1-SNAPSHOT</version>
 6   <dependencies>
 7   
 8   <dependency>
 9     <groupId>org.seleniumhq.selenium</groupId>
10     <artifactId>selenium-java</artifactId>
11     <version>3.4.0</version>
12 </dependency>
13 
14    <!-- https://mvnrepository.com/artifact/org.testng/testng -->
15 <dependency>
16     <groupId>org.testng</groupId>
17     <artifactId>testng</artifactId>
18     <version>6.9.10</version>
19     <scope>test</scope>
20 </dependency>
21 
22 <!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
23 <dependency>
24     <groupId>dom4j</groupId>
25     <artifactId>dom4j</artifactId>
26     <version>1.6.1</version>
27 </dependency>
28 
29 
30   <!-- https://mvnrepository.com/artifact/log4j/log4j -->
31 <dependency>
32     <groupId>log4j</groupId>
33     <artifactId>log4j</artifactId>
34     <version>1.2.17</version>
35 </dependency>
36 
37   <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
38 <dependency>
39     <groupId>org.apache.poi</groupId>
40     <artifactId>poi-ooxml</artifactId>
41     <version>3.17</version>
42 </dependency>
43 
44   </dependencies>
45   <build>
46     <plugins>
47     <!-- 1:解決每次右鍵項目名-maven->update project 時候,項目jdk版本變了,變回1.5版本或者其餘版本 
48     2: 解決使用maven編譯其餘問題:如提示不能在內部類訪問外部非final局部變量 
49     -->
50         <plugin>
51             <groupId>org.apache.maven.plugins</groupId>
52             <artifactId>maven-compiler-plugin</artifactId>
53             <version>3.5.1</version>
54             <configuration>
55                 <source>1.8</source>
56                 <target>1.8</target>
57                 <encoding>UTF-8</encoding>
58             </configuration>
59         </plugin> 
60     </plugins>
61 </build>
62   
63 </project>
View Code

 

2.按照上面項目結構圖來建立一個maven工程

3.封裝driver的方法,以前的博客中已經詳細說明,這裏再也不贅述。直接上代碼

實現一個讀取瀏覽器驅動配置文件的幫助類

  1 package com.demo.auto.claire.util;
  2 
  3 
  4 
  5 import java.util.List;
  6 import org.dom4j.Document;
  7 import org.dom4j.DocumentException;
  8 import org.dom4j.Element;
  9 import org.dom4j.io.SAXReader;
 10 import org.openqa.selenium.WebDriver;
 11 import org.openqa.selenium.remote.DesiredCapabilities;
 12 
 13 public class SeleniumUtil {
 14     private static Class clazz;
 15     private static Object obj ;
 16     
 17     public static void main(String[] args) {
 18         getDriver();
 19     }
 20     
 21 
 22 
 23     public static WebDriver getDriver() {
 24         Document document = null;
 25         Element driverNameElement= null;
 26         String driverName =null;
 27         
 28         SAXReader reader = new SAXReader();
 29         try {
 30             document = reader.read(SeleniumUtil.class.getResourceAsStream("/driverProperties.xml"));
 31         } catch (DocumentException e) {
 32 
 33             e.printStackTrace();
 34         }
 35 
 36         /**
 37          * 下面是經過解析XML,獲取到驅動的類全名
 38          */
 39         Element rootElement = document.getRootElement(); //獲取到根節點
 40         int index = Integer.parseInt(rootElement.attributeValue("driverIndex"));//獲取到根節點上的driverIndex並轉成int類型
 41         
 42         
 43         //獲取到全部"name"子節點,遍歷,找出與根節點中的driverIndex相同的,將其value屬性值獲取出來,做爲類全名用於反射
 44         List<Element> driverNameElements = rootElement.elements("name");
 45         for (Element driverNameElement1 : driverNameElements) {
 46             int i = Integer.parseInt(driverNameElement1.attributeValue("index"));
 47             if (i == index) {
 48                 driverName = driverNameElement1.attributeValue("value");//獲取到name子節點的「value」屬性值
 49                 driverNameElement = driverNameElement1;//將該節點賦值給driverElement,後續根據它來得到子節點
 50             }
 51             
 52         }
 53         
 54 
 55         /**
 56          * 經過類全名,反射出驅動類來
 57          */
 58         try {
 59             clazz = Class.forName(driverName);
 60         } catch (ClassNotFoundException e) {
 61 
 62             e.printStackTrace();
 63         }
 64         
 65         /**
 66          * 下面是解析XML中的系統參數以及能力參數
 67          */
 68         
 69         Element propertiesElement = driverNameElement.element("properties");
 70         List<Element> propertyElements = propertiesElement.elements("property");
 71 
 72         //設置系統參數
 73         for (Element property : propertyElements) {
 74             
 75             System.setProperty(property.attributeValue("name"), property.attributeValue("value"));
 76             
 77         }
 78         
 79         //設置能力(ie的話,須要設置忽略域設置等級 以及忽略頁面百分比的能力)
 80         Element capabilitiesElement = driverNameElement.element("capabilities");
 81         if (capabilitiesElement != null) {
 82             //建立能力對象
 83             DesiredCapabilities realCapabilities = new DesiredCapabilities();
 84             //得到能力列表
 85             List<Element> capabilitiesElements = capabilitiesElement.elements("capability");
 86             for (Element capability : capabilitiesElements) {
 87                 //遍歷能力列表,並給能力賦值
 88                 realCapabilities.setCapability(capability.attributeValue("name"), true);
 89             }
 90         }
 91         
 92         
 93         /*
 94          * 經過反射,建立驅動對象
 95          */
 96         
 97         try {
 98             obj = clazz.newInstance();
 99         } catch (InstantiationException e) {
100             e.printStackTrace();
101         } catch (IllegalAccessException e) {
102             e.printStackTrace();
103         }
104         
105         WebDriver driver = (WebDriver) obj;
106         return driver;
107     }
108 
109 }
View Code

下面是驅動器配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!--  只須要修改下面的driverIndex 就能夠去建立對應index的驅動-->
<driver driverIndex="0">

    <!-- 谷歌瀏覽器配置文件 -->
    <name value="org.openqa.selenium.chrome.ChromeDriver" index="0">
        <properties>
            <property
                name="ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY"
                value="E:/driver/chromedriver.exe" />
        </properties>
    </name>

    <!-- 火狐瀏覽器 對應的selenium3.x版本 的配置文件 -->
    <name value="org.openqa.selenium.firefox.FirefoxDriver"
        seleniumVersion="3.x" index="1">
        <properties>
            <property name="SystemProperty.BROWSER_BINARY"
                value="C:\Program 
        Files (x86)\Mozilla Firefox\firefox.exe" />
            <property
                name="GeckoDriverService.GECKO_DRIVER_EXE_PROPERTY"
                value="E:/driver/geckodriver.exe" />
        </properties>
    </name>

    <!-- 火狐瀏覽器 對應的selenium2.x版本 的配置文件 -->
    <name value="org.openqa.selenium.firefox.FirefoxDriver"
        seleniumVersion="2.x" index="2">
        <properties>
            <property name="SystemProperty.BROWSER_BINARY"
                value="C:\Program 
        Files (x86)\Mozilla Firefox\firefox.exe" />
        </properties>
    </name>

    <!--IE瀏覽器配置文件 -->
    <name value="org.openqa.selenium.ie.InternetExplorerDriver"
        index="3">
        <properties>
            <property
                name="InternetExplorerDriverService.IE_DRIVER_EXE_PROPERTY"
                value="E:/driver/IEDriverServer.exe" />
        </properties>
        <capabilities>
            <capability
                name="InternetExplorerDriver.IGNORE_ZOOM_SETTING" />
            <capability
                name="InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS" />
        </capabilities>
    </name>
</driver>
View Code

 

4.log4j的配置文件

###根logger設置###
log4j.rootLogger = INFO,console,file

### 輸出信息到控制檯###
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.Threshold = info 
log4j.appender.console.layout.ConversionPattern = [%p] %d{yyyy-MM-dd HH:mm:ss} method: %l----%m%n

###輸出INFO 級別以上的日誌文件設置###
log4j.appender.file = org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File = E:/log/web.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = info 
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} method: %l - [ %p ]----%m%n
View Code

 

5.將測試數據寫到excel中,並提供一個讀取excel的幫助類

 

 使用poi讀取excel。讀取出來的數據,能夠封裝成二維數組object[][]或者iterator(object[]),做爲數據提供者,給測試用例使用。

因爲二維數組的方式是一次性將全部的測試用例所有讀出來的,當測試過程當中出現問題或者數據量巨大的時候,再使用這種方式,會形成內存浪費。筆者建議你們使用迭代器的方式來實現。

下面是迭代器的方式(推薦使用):

package com.demo.auto.claire.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;


/**
 * 解析存放到excel中的測試數據,提供給測試case使用。
 * 讀取的excel名稱是取測試case的前半部分,如Register_SuccessTester_002,取的是 Register
 * 因此測試數據的excel取名必須注意!!!
 * @author cuijing
 *
 */
public class ExcelDataProvider implements Iterator<Object[]> {
    /**
     * example,測試使用:
     */
    public static void main(String[] args) throws EncryptedDocumentException, InvalidFormatException, IOException {
        ExcelDataProvider dataProvider = new ExcelDataProvider("002","RegisterTester.xls");
        while(dataProvider.hasNext()) {
            dataProvider.next();
        }
    }
    
    
    
    Iterator<Row> itRow;
    Row currentRow ;    
    Cell currentCell;
    String[] cloumnName;
    
    

    /**
     *該構造方法,經過指定的excel和該excel的指定sheet初始化列名數組 初始化行迭代器
     * @param index
     */
    public ExcelDataProvider(String sheetName,String fileName)  {
        InputStream inp = ExcelDataProvider.class.getResourceAsStream("/testData/"+fileName+".xls");

        Workbook workbook =null;
        try {
            workbook  = WorkbookFactory.create(inp);
        } catch (EncryptedDocumentException | InvalidFormatException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        Sheet sheet = workbook.getSheet(sheetName);
        
        
        itRow = sheet.iterator();
        if (itRow.hasNext()) {
            //獲取到第一行
            currentRow = itRow.next();
            int length = currentRow.getLastCellNum();
            cloumnName=new String[length];
            
            /*//獲取到第一行的每一個單元格內容
            for (int i = 0; i < cloumnName.length; i++) {
                cloumnName[i] = currentRow.getCell(i+1).getStringCellValue();
            }*/
            
            //獲得第一行的迭代器
            Iterator<Cell> cellIt = currentRow.iterator();
            //將第一行的數據,填充到列名數組中
            for (int i = 0; cellIt.hasNext(); i++) {
                currentCell=cellIt.next();
                cloumnName[i]=currentCell.getStringCellValue();
            }
            System.out.println(Arrays.toString(cloumnName));
            
        }
    
        
        
        
    }
    
/**
 * 經過行迭代器判斷是否還有下一行
 */
    @Override
    public boolean hasNext() {
        if (itRow.hasNext()) {
            return true;
        }
        return false;
    }

    /**
     * 經過行迭代器獲取到下一行,遍歷下一行的全部單元格,將數據放到map中,再將map包裝成object的數組
     */
    @Override
    public Object[] next() {
        //指向下一行
        currentRow = itRow.next();
        //遍歷該行的單元格,並將單元格數據填充到map中
        Map<String, String> map = new LinkedHashMap<>();
        for (int i = 0; i < cloumnName.length; i++) {
            currentCell = currentRow.getCell(i, MissingCellPolicy.CREATE_NULL_AS_BLANK);
            currentCell.setCellType(CellType.STRING);
            map.put(cloumnName[i], currentCell.getStringCellValue());
        }
        Object[] objects = {map};
        
        System.out.println(map);
        return objects;
    }

}
View Code

下面是二維數組的方式:

package com.claire.jing.utils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

public class ExcelDataProviderPOI {
    public static void main(String[] args) throws EncryptedDocumentException, InvalidFormatException, IOException {
        Object[][] objects = getDataProvider("001");
        
    }
    
    
    
    public static Object[][] getDataProvider(String sheetName) throws EncryptedDocumentException, InvalidFormatException, IOException {
        Object[][] objects =null;
        
        InputStream inp = ExcelDataProviderPOI.class.getResourceAsStream("/testData/competitionSearch.xls");
        
        Workbook workbook = WorkbookFactory.create(inp);
        
        Sheet  sheet = workbook.getSheet(sheetName);
        int rowNum = sheet.getLastRowNum();
        objects = new Object[rowNum][];
        
        for (int i = 1; i <=rowNum; i++) {
            int columnNum = sheet.getRow(i).getLastCellNum();
            System.out.println("第"+i +"行有"+columnNum+"列數據");
            objects[i-1] = new Object[columnNum];
            
            for (int j = 0; j < columnNum; j++) {
                sheet.getRow(i).getCell(j).setCellType(CellType.STRING);
                objects[i-1][j] = sheet.getRow(i).getCell(j,MissingCellPolicy.RETURN_NULL_AND_BLANK).getStringCellValue();
            }
            
        }
        System.out.println(Arrays.deepToString(objects));
        return objects;
        
        
    }

}
View Code

 

 6.將頁面元素的定位信息寫到xml中,並提供相應的解析幫助類,將其映射到指定對象中。

下面是xml文件

<?xml version="1.0" encoding="UTF-8"?>
<pages>
<!-- 代碼是經過下面的pageName來定位去讀取哪一個頁面的定位信息
pageName是取測試case的類名前半部分
如Register_SuccessTester_002,取的是 Register-->

<!-- 註冊頁面 -->
<page pageName="Register">
<locaters>
<locator by="id" value="mobilephone" des="手機號輸入框"></locator>
<locator by="id" value="password" des="密碼輸入框"></locator>
<locator by="id" value="pwdconfirm" des="確認密碼輸入框"></locator>
<locator by="id" value="signup-button" des="註冊按鈕"></locator>
<locator by="className" value="tips" des="提示信息"></locator>
</locaters>
</page>

<!-- 登陸頁面 -->
<page pageName="Login">
<locaters>
<locator by="id" value="mobilephone" des="手機號輸入框"></locator>
<locator by="id" value="password" des="密碼輸入框"></locator>
<locator by="id" value="login" des="登陸按鈕"></locator>
<locator by="id" value="reset" des="重置按鈕"></locator>
</locaters>
</page>



</pages>
View Code

下面是xml文件的解析幫助類

package com.demo.auto.claire.util;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.demo.auto.claire.pageLocator.Locator;
/**
 * 1.解析頁面定位信息的Url,將解析內容裝到map中
 * <代碼是經過下面的pageName來定位去讀取哪一個頁面的定位信息
 * pageName是取測試case的類名前半部分如Register_SuccessTester_002,取的是 Register
 * 因此頁面定位信息的xml文件 取名必須注意!!!
 * 
 * 2.將項目中用到的測試頁面都寫到一個xml中的,因此固定讀取的是 /locatorxml/locater.xml
 * @author cuijing
 *
 */

public class LocatorUtil {
    public static void main(String[] args) {
        Locator locator = getLocator("RegisterTester", "手機號輸入框");
        System.out.println(locator.getBy());
    }
    
    //將頁面元素的定位信息存儲在map中
    //經過頁面名稱,獲取到指定頁面的全部定位信息
    //經過定位元素的描述,獲取到locator對象
    private static Map<String,Map<String, Locator>> pageMap = new HashMap<>();
    
    //該類第一次被使用,就會去讀取指定xml文件(讀取頁面的全部元素定位信息)
        static {
            readXml();
        }
        
    

    
    
    /**
     * 
     * @param pageName
     * 頁面名稱
     * @param desc
     * 元素的關鍵字描述
     * @return
     */
    public static Locator getLocator(String pageName,String desc) {
        //獲取到指定頁面的全部定位信息
        
        Map<String, Locator> map = pageMap.get(pageName);
        //System.out.println("經過頁面名稱獲取到頁面全部定位信息"+map);
        //返回指定描述locator對象
        Locator locator = map.get(desc);
        //System.out.println("經過關鍵字"+desc +"獲取到locator對象"+locator);
        return locator; 
        }
    

    /**
     * 讀取xml文件
     */
    public static void readXml() {
        SAXReader reader = new SAXReader();
        Document doucment = null;
        try {
            doucment = reader.read(LocatorUtil.class.getResourceAsStream("/locatorxml/locater.xml"));
        } catch (DocumentException e) {
            System.out.println("讀取xml失敗");
            e.printStackTrace();
        }
        //獲取到根節點pages
        Element rootEleent = doucment.getRootElement();
        //System.out.println("獲取到根節點"+rootEleent.getName());
        //獲取到子節點page
        List<Element> elements = rootEleent.elements();
        //System.out.println("獲取到"+elements.size()+"個頁面");
        
        
        //遍歷全部的page
        for (Element element : elements) {
            String pageName = element.attribute("pageName").getStringValue();
            //System.out.println("pageName爲" +pageName);
            
            
            
            
            //獲取到page的子節點 locators
            Element locatorsElement = element.element("locaters");
            //獲取到全部的locator
            List<Element> locatorElements = locatorsElement.elements();
            
            //建立map
            Map<String, Locator> LocatorMap = new HashMap<>();
            for (Element locatorElement : locatorElements) {                
                Locator locatorObj = new Locator(locatorElement.attributeValue("by"),locatorElement.attributeValue("value"),locatorElement.attributeValue("des"));
                //System.out.println(locatorObj);
                
                
                //經過描述信息,獲取到locator對象
                LocatorMap.put(locatorElement.attributeValue("des"), locatorObj);
            }
            
            pageMap.put(pageName, LocatorMap);
            
            
            System.out.println(pageMap);
            
        }
        
        
        
        
    }
    

}
View Code

解析完成後映射到Locator類:

package com.demo.auto.claire.pageLocator;

public class Locator {
    String by;
    String value;
    String desc;
    //getter setter
    public String getBy() {
        return by;
    }
    public void setBy(String by) {
        this.by = by;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
    
    //construction
    public Locator(String by, String value, String desc) {
        super();
        this.by = by;
        this.value = value;
        this.desc = desc;
    }
    
    //toString
    @Override
    public String toString() {
        return "Locator [by=" + by + ", value=" + value + ", desc=" + desc + "]";
    }
    
    

}
View Code

 

 

7.BaseTester的設計

在每一個測試進行以前,都要啓動瀏覽器驅動--------將啓動瀏覽器放到baseTester中

在每次測試執行以後,都要退出瀏覽器驅動,並關閉全部的相關窗口------------------將退出驅動放到BaseTester中

 使用智能等待的方式獲取元素,在獲取元素的時候,使用xml文件的解析幫助類提供的方法,獲取到Locator類,並經過反射獲取到對應的定位元素方法。

能夠對一些selenium的簡單方法進行二次封裝,使得測試腳本更加簡潔,後續維護也更加簡單。

還能夠實現一個檢查點技術(其實就是Assert,封裝本身須要的assert方法)

下面是筆者部分的BaseTester代碼,能夠根據須要自行封裝。

package com.demo.auto.claire.base;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;

import org.apache.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.DataProvider;

import com.demo.auto.claire.pageLocator.Locator;
import com.demo.auto.claire.util.ExcelDataProvider;
import com.demo.auto.claire.util.LocatorUtil;
import com.demo.auto.claire.util.PropertiesUtil;
import com.demo.auto.claire.util.SeleniumUtil;


public class BaseTester {
    public static WebDriver driver;
    static Map<String, Map<String, Locator>> pagesMap;
    Logger logger= Logger.getLogger(BaseTester.class);
    
    /**
     * 套件執行前,啓動瀏覽器驅動
     */
    @BeforeSuite
    public void BeforSuit() {
        driver = SeleniumUtil.getDriver();
    }
    
    /**
     * 套件執行後,退出當前瀏覽器驅動,關閉全部相關窗口
     * @throws InterruptedException
     */
    @AfterSuite
    public void afterSuit() throws InterruptedException {
        Thread.sleep(5000);
        driver.quit();
    }
    

    /****************************數據提供者*****************************************/
    
    public static void main(String[] args) {
        String className = "Register_FailTester_001";
        int firstIndex = className.indexOf("_");
        int lastIndex = className.lastIndexOf("_");
        System.out.println(lastIndex);
        

        //獲取到當前excel的名稱(規定excel的名稱與咱們的類名第一個「_」前字符串相同)
        String excelName = className.substring(0, firstIndex);
        System.out.println("當前頁面名稱爲"+excelName);
        String sheetName =  className.substring(lastIndex+1);
        System.out.println("當前sheet名稱爲"+sheetName);
    }
    
    
    /**
     * 數據提供者。ExcelDataProvider是經過poi從excel讀取的測試數據
     * @return
     */
    @DataProvider(name="dataProvider")
    public Iterator<Object[]> dataProvider(){
        String className = this.getClass().getSimpleName();
        int firstIndex = className.indexOf("_");
        int lastIndex = className.lastIndexOf("_");
        
        //獲取到當前excel的名稱(規定excel的名稱與咱們的類名第一個「_」前字符串相同)
        String excelName = className.substring(0, firstIndex);
    //    System.out.println("當前excel名稱爲"+excelName);
        String sheetName =  className.substring(lastIndex+1);
    //    System.out.println("當前sheet名稱爲"+sheetName);
        return new ExcelDataProvider(sheetName,excelName);
        
    }
    
    
    
    /*****************************智能等待獲取元素*****************************/
    
    /**
     * 獲取元素
     * @param timeOut
     * 超時時間
     * @param by
     * 獲取元素方法
     * @return
     * 返回獲取到的元素
     */
    public WebElement getElement(int timeOut,By by) {
        
        WebDriverWait wait = new WebDriverWait(driver, timeOut);
        return wait.until(new ExpectedCondition<WebElement>() {

            @Override
            public WebElement apply(WebDriver driver) {
                
                return driver.findElement(by);
            }
        });
        
        
    }
    
    /**
     * 獲取元素,指定等待時間爲5S
     * @param by
     * 獲取元素 方法
     * @return
     */
    public WebElement getElement(By by) {
        
        return getElement(10, by);
        
    }

    /**
     * 經過關鍵字定位元素
     * @param timeOut
     * 超時時間
     * @param keyword
     * 元素關鍵字,經過該關鍵字定位到元素
     * @return
     */

    public WebElement getElement(int timeOut,String keyword) {
        //經過反射,獲取到運行該方法對象的類名
        //誰繼承了該BaseTester類就獲取到誰的類名
        String className = this.getClass().getSimpleName();
        int index = className.indexOf("_");
        String pageName = className.substring(0, index);
        //System.out.println("當前頁面名稱爲"+pageName);
        WebElement element = getElement(pageName, keyword, timeOut);
        return element;
    }
    
    /**
     * 獲取指定頁面名稱和關鍵字的元素
     * @param pageName
     * 頁面名稱
     * @param keyword
     * 元素關鍵字
     * @param timeOut
     * 超時時間
     * @return
     * WebElement,返回查找到的元素
     */
    
    public WebElement getElement(String pageName,String keyword,int timeOut) {
        //locatorUtil類讀取xml,並提供獲取Locator的方法,locator對象裏裝的是by,value,desc
        //是經過當前運行對象的類名來當作pageName的
        Locator locator = LocatorUtil.getLocator(pageName, keyword);
        
        //獲取到定位方式
        String byStr = locator.getBy();//這個對應到By類中的方法名,如id,name等(8大定位方法)
        String value = locator.getValue();//對應定位的值,如:By.id(value)
        
        WebDriverWait wait = new WebDriverWait(driver, timeOut);
        return wait.until(new ExpectedCondition<WebElement>() {

            @Override
            public WebElement apply(WebDriver driver) {
                By by = null;
                //經過反射,獲取到By類的字節碼文件。
                Class<By> clazz = By.class;
                
                try {
                    //經過反射,獲取到指定名稱的方法。
                    Method method = clazz.getDeclaredMethod(byStr, String.class);
                    //調用獲取到的方法。因爲By中的方法是靜態的,經過類調用。這裏的對象爲null。invoke以後返回的是object對象,強制類型轉換爲By對象
                    by = (By) method.invoke(null, value);
                }catch (Exception e) {
                    e.printStackTrace();
                }
                return driver.findElement(by);
            }
        });
    
        
    }
    
    
    
    
    
    
/**
 * 經過關鍵字定位元素,固定等待時間爲5S(獲取的是當前頁面的元素)
 * @param keyword
 * 元素關鍵字,經過該關鍵字定位到元素
 * @return
 */
    public WebElement getElement(String keyword) {
        
        return getElement(5, keyword);
        
    }
    
    
    
    
    
    /************************selenium簡單方法的二次封裝***********************************/
    
    
    /**
     * 向指定元素輸入內容,對sendKeys方法的二次包裝
     * @param keyWord
     * 元素關鍵字,經過該關鍵字定位到元素
     * @param context
     * 要輸入的內容
     */
    public void sendkeys(String keyword,String context) {
        WebElement targetElement = null;
        try {
            targetElement = getElement(keyword);
            targetElement.sendKeys(context);
            logger.info("成功向元素"+targetElement +"輸入內容"+context);
        } catch (Exception e) {
            logger.error("向元素"+targetElement +"輸入內容"+context+"失敗");
            e.printStackTrace();
        }
    }
    
    
    /**
     * 獲取到指定元素,並點擊
     * @param keyword
     * 元素關鍵字,經過該關鍵字定位到元素
     */
    public void click(String keyword) {
        WebElement targetElement=null;
        try {
            targetElement = getElement(keyword);
            targetElement.click();
            logger.info("點擊元素"+targetElement);
        } catch (Exception e) {
            logger.error("點擊元素"+targetElement+"失敗");
            e.printStackTrace();
        }
    }    
    
    /**
     * 獲取指定元素上的文本信息
     * @param keyword
     * 元素關鍵字,經過該關鍵字定位到元素
     * @return
     */
    public String getText(String keyword) {
        WebElement targetElement = null;
        String text = null;
        try {
            targetElement = getElement(keyword);
            logger.info("獲取元素文本信息");
            text = targetElement.getText();
        } catch (Exception e) {
            logger.error("獲取元素文本信息失敗");
            e.printStackTrace();
        }
        return text;
    }
    
    public void toUrl(String propertiesKey) {
        String url = PropertiesUtil.getProperties(propertiesKey);
        driver.get(url);
        
    }
    
    
    /**********************************************檢查點Assert************************************************************/
    
    /**
     * 檢查元素文本與預期文本是否相同
     * @param keyword 
     * 元素關鍵字,經過該關鍵字定位到元素
     * @param expected
     * 預期文本
     */
    public void assertTextEquals(String keyword,String expected) {
        WebElement targetElement = getElement(keyword);
        String actual = targetElement.getText();
        Assert.assertEquals(actual, expected);
    }
    
    
    public void assertCanGetPointElement(String pageName,String keyword) {
        WebElement targetElement = getElement(pageName,keyword,5);
        Assert.assertFalse(targetElement==null);
    }
    
    
    
    
    
    /*********************************************************/
    
}
View Code

 

 上述BaseTester類中,封裝了一個打開指定url的方法,其中是將url寫到了url.properties文件中,並提供瞭解析幫助類。

url.properties文件

register=http://XXXX.html
login=http://XXXX.html
View Code

解析幫助類:

package com.demo.auto.claire.util;

import java.io.IOException;
import java.util.Properties;
/**
 * 該類專門用來處理properties文件
 * @author cuijing
 *
 */
public class PropertiesUtil {
    public static void main(String[] args) {
        System.out.println(getProperties("register"));
    }
    
    
    static Properties properties = new Properties();
    
    //只加載一次配置文件
    static {
        readURLProperties();
    }
    
    /**
     * 加載配置文件
     */
    private static void readURLProperties() {

        try {
            properties.load(PropertiesUtil.class.getResourceAsStream("/properties/url.properties"));
            
        } catch (IOException e) {
            
            e.printStackTrace();
        }
    }
    
    /**
     * 經過配置文件中的 key獲取到value
     * @param propertiesKey
     * 配置文件中的key
     * @return
     */
    public static String getProperties(String propertiesKey) {
        return properties.getProperty(propertiesKey);
    }

}
View Code

 

 

5.編寫測試cese

編寫測試用例的時候,一般都分爲正向測試用例和反向測試用例。將登陸頁面的測試case分爲Register_FailTester_001和Register_SuccessTester_002。

每個測試腳本都繼承自BaseTester

注意:該類名的第一個字段Register 必須與測試數據的excel文件和頁面定位信息中的pageName一致,第三個字段001或002必須與測試數據的excel中的sheet同名。

方便後期靈活的去讀取相應的測試數據和元素定位信息

 

 反向測試腳本:

package com.demo.auto.claire.testcase.register;


import java.util.Map;

import org.testng.annotations.Test;

import com.demo.auto.claire.base.BaseTester;


public class Register_FailTester_001 extends BaseTester{
    
    
    /**
     * 註冊失敗的用例
     * @param data
     * @throws InterruptedException
     */
    @Test(dataProvider="dataProvider")
    public void test_register_failure_001(Map<String, String> data) throws InterruptedException {
        //到指定頁面去
        toUrl("register");
        //向手機號碼輸入框輸入測試數據
        sendkeys("手機號輸入框", data.get("用戶名"));
        sendkeys("密碼輸入框", data.get("密碼"));
        sendkeys("確認密碼輸入框", data.get("確認密碼"));
        //點擊註冊按鈕
        click("註冊按鈕");
        
        
        Thread.sleep(1000);
        //斷言用例是否成功
        assertTextEquals("提示信息", data.get("預期結果"));
        
    }
    

}
View Code

 

正向測試腳本:

package com.demo.auto.claire.testcase.register;

import java.util.Map;

import org.testng.annotations.Test;

import com.demo.auto.claire.base.BaseTester;

public class Register_SuccessTester_002 extends BaseTester{
    
    /**
     * 註冊成功的用例
     * @param data
     * @throws InterruptedException
     */
    @Test(dataProvider="dataTestersuccess",enabled=false)
    public void test_register_success_002(Map<String, String> data) throws InterruptedException {
        toUrl("register");
        sendkeys("手機號輸入框", data.get("用戶名"));
        sendkeys("密碼輸入框", data.get("密碼"));
        sendkeys("確認密碼輸入框", data.get("確認密碼"));
        
        click("註冊按鈕");//此時會跳轉到登陸頁面
        
        
        Thread.sleep(1000);//等待頁面成功跳轉
        assertCanGetPointElement("LoginTester", "登陸按鈕");
    }

}
View Code

 能夠看到,此時的測試腳本很是的清晰易讀。

 

該框架還未實現截圖、發郵件、定時跑的功能。後期更新。。。

相關文章
相關標籤/搜索