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>
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 }
下面是驅動器配置文件:
<?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>
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
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; } }
下面是二維數組的方式:
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; } }
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>
下面是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); } } }
解析完成後映射到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 + "]"; } }
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); } /*********************************************************/ }
上述BaseTester類中,封裝了一個打開指定url的方法,其中是將url寫到了url.properties文件中,並提供瞭解析幫助類。
url.properties文件
register=http://XXXX.html
login=http://XXXX.html
解析幫助類:
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);
}
}
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("預期結果")); } }
正向測試腳本:
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", "登陸按鈕"); } }
能夠看到,此時的測試腳本很是的清晰易讀。
該框架還未實現截圖、發郵件、定時跑的功能。後期更新。。。