webUI自動化測試框架(二):程式碼分層-基礎層
阿新 • • 發佈:2019-01-24
前言:該webUI自動化框架主要分為四層:基礎層、物件層、操作層、用例層,每一層負責各自的功能,這樣有益於提高程式碼的可讀性,複用性和擴充套件性。基礎層主要封裝了一些工具類,如解析xml檔案,讀取excel,分瀏覽器啟動,時間處理等,供其他類呼叫。
另外,筆者這邊主要使用的第三方庫有:
TestNG:負責斷言、測試指令碼的管理以及輸出測試報告,安裝及使用教程見筆者的另一篇部落格:http://blog.csdn.net/u010798968/article/details/73549612
log4j:負責生成日誌
dom4j:解析xml
程式碼大致結構如下圖所示:
進入正題,介紹基礎層各個核心類。
1.UIExecutor為一個介面,包含了若干個抽象方法,這些方法都是webdriver中常用的操作方法,如點選,獲取文字,切換視窗等,後續有需要擴充套件的頁面操作都可以在該介面中定義。
2.UIExecutor介面的實現類UIExecutorImpl:package com.etyero.utils; import org.openqa.selenium.WebElement; import com.etyero.object.Locator; /** * webDriver常見的API * * @author ljl */ public interface UIExecutor { //點選 public void click(Locator locator); //輸入文字 public void sendKey(Locator locator,String value); //獲取元素文字 public String getText(Locator locator); //獲取元素 public WebElement getElement(Locator locator) throws Exception; //判斷元素是否顯示 public boolean isElementDisplayed(Locator locator); //切換頁面 public void switchWindow(String title); //切換frame public void switchFrame(Locator locator); //智慧等待 public void waitElement(Locator locator); }
package com.etyero.utils; import java.util.Set; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import com.etyero.object.Locator; /** * UIExecutor介面實現類 * * @author ljl */ public class UIExecutorImpl implements UIExecutor { private WebDriver driver; public LogUtil log; public UIExecutorImpl(WebDriver driver) { this.driver = driver; } public WebDriver getDriver() { return driver; } public void setDriver(WebDriver driver) { this.driver = driver; } /** * 點選元素 * * @author ljl * @param locator */ public void click(Locator locator) { WebElement element = getElement(locator); element.click(); } /** * 輸入文字 * * @author ljl */ @Override public void sendKey(Locator locator, String value) { WebElement element = getElement(locator); element.clear(); element.sendKeys(value); } @Override public String getText(Locator locator) { WebElement element = getElement(locator); return element.getText(); } /** * 獲取元素 * * @author ljl * */ @Override public WebElement getElement(Locator locator) { WebElement element = null; String address = locator.getAddress(); // long tinkTime = locator.getWaitSec() * 1000; // try { // // 思考時間,等待元素載入 // Thread.sleep(tinkTime); // } catch (InterruptedException e) { // e.printStackTrace(); // } switch (locator.getByType()) { case xpath: element = driver.findElement(By.xpath(address)); break; case id: element = driver.findElement(By.id(address)); break; case className: element = driver.findElement(By.className(address)); break; case linkText: element = driver.findElement(By.linkText(address)); break; default: break; } return element; } /** * 元素是否顯式顯示 * * @author ljl */ @Override public boolean isElementDisplayed(Locator locator) { boolean flag = false; WebElement element = getElement(locator); flag = element.isDisplayed(); return flag; } /** * 切換視窗 * * @author ljl */ @Override public void switchWindow(String title) { Set<String> handles = driver.getWindowHandles(); for (String handle : handles) { if (handle.equals(driver.getWindowHandle())) { continue; } else { driver.switchTo().window(handle); if (title.contains(driver.getTitle())) { break; } else { continue; } } } } /** * 切換frame * * @author ljl */ @Override public void switchFrame(Locator locator) { driver.switchTo().frame(locator.getAddress()); } /** * 智慧等待,超過該時長丟擲異常 * * @author ljl */ @Override public void waitElement(Locator locator) { // TODO Auto-generated method stub } }
3.BrowserUtil,瀏覽器工具類,用來返回不同的瀏覽器driver:
package com.etyero.utils;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
public class BrowserUtil {
private static WebDriver driver;
/**
* 啟動ie瀏覽器
*
* @param browserDriverUrl
* 瀏覽器驅動url
* @param sec
* 所有頁面操作的等待超時時長,此處為隱式等待,超時後找不到元素則丟擲異常NoSuchElementException
* @author ljl
*/
public static WebDriver ie(String browserDriverUrl, long sec) {
System.setProperty("webdriver.ie.driver", browserDriverUrl);
// 關閉IE保護模式
DesiredCapabilities ieCapabilities = DesiredCapabilities.internetExplorer();
ieCapabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
driver = new InternetExplorerDriver(ieCapabilities);
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(sec, TimeUnit.SECONDS);
return driver;
}
/**
* 啟動chrome瀏覽器
*
* @param browserDriverUrl
* 瀏覽器驅動url
* @param sec
* 所有頁面操作的等待超時時長
* @author ljl
*/
public static WebDriver chrome(String browserDriverUrl, long sec) {
System.setProperty("webdriver.chrome.driver", browserDriverUrl);
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(sec, TimeUnit.SECONDS);
return driver;
}
/**
* 啟動fireFox瀏覽器
*
* @param browserDriverUrl
* 瀏覽器驅動url
* @param sec
* 所有頁面操作的等待超時時長
* @author ljl
*/
public static WebDriver fireFox(String browserDriverUrl, long sec) {
System.setProperty("webdriver.firefox.bin", browserDriverUrl);
driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(sec, TimeUnit.SECONDS);
return driver;
}
/**
* 啟動htmlUnitDriver,不會開啟實際遊覽器,執行速度快 但當頁面有負責js時,會定位不到元素,不建議使用
*
* @author ljl
*/
public static WebDriver htmlUnitDriver(long sec) {
driver = new HtmlUnitDriver();
driver.manage().timeouts().implicitlyWait(sec, TimeUnit.SECONDS);
return driver;
}
}
4.XMLUtil,因為我們的頁面元素主要在xml檔案中維護,這樣可以做到頁面元素和程式碼分離,如果頁面元素有變,我們只需要修改xml檔案即可,無需到程式碼中去一個個找,所以需要此工具類去解析xml檔案得到頁面元素的相關資訊:
package com.etyero.utils;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.etyero.object.Locator;
import com.etyero.object.Locator.ByType;
public class XMLUtil {
/**
* 讀取頁面配置檔案
*
* @author ljl
* @param xmlUrl
* 頁面配置檔案路徑
* @param pageName
* 頁面名稱
*/
public static HashMap<String, Locator> readXMLDocument(String xmlUrl, String pageName) throws Exception {
LogUtil log = new LogUtil(XMLUtil.class);
HashMap<String, Locator> locatorMap = new HashMap<>();
File file = new File(xmlUrl);
if (!file.exists()) {
log.error("can't find " + xmlUrl);
} else {
// 建立SAXReader物件
SAXReader sr = new SAXReader();
// 讀取xml檔案轉換為Document
Document document = sr.read(file);
// 獲取所有根節點元素物件
Element root = document.getRootElement();
Iterator<?> rootIte = root.elementIterator();
Locator locator = null;
// 遍歷根節點
while (rootIte.hasNext()) {
Element page = (Element) rootIte.next();
log.info("pageName is " + pageName);
// 忽略大小寫比較
if (page.attribute(0).getValue().equalsIgnoreCase(pageName)) {
Iterator<?> pageIte = page.elementIterator();
// 找到pageName後遍歷該page內各個節點
while (pageIte.hasNext()) {
String type = "";
String timeOut = "3";
String value = "";
String locatorName = "";
Element locatorEle = (Element) pageIte.next();
Iterator<?> locatorIte = locatorEle.attributeIterator();
// 遍歷單個標籤內的元素
while (locatorIte.hasNext()) {
Attribute attribute = (Attribute) locatorIte.next();
String attributeName = attribute.getName();
if (attributeName.equals("type")) {
type = attribute.getValue();
} else if (attributeName.equals("timeOut")) {
timeOut = attribute.getValue();
} else {
value = attribute.getValue();
}
}
locator = new Locator(value, Integer.parseInt(timeOut), getByType(type));
locatorName = locatorEle.getText();
locatorMap.put(locatorName, locator);
}
break;
}
}
}
return locatorMap;
}
/**
* 轉換元素定位型別
*
* @author ljl
*/
public static ByType getByType(String type) {
ByType byType = ByType.xpath;
if (type == null || type.equalsIgnoreCase("xpath")) {
byType = ByType.xpath;
} else if (type.equalsIgnoreCase("id")) {
byType = ByType.id;
} else if (type.equalsIgnoreCase("name")) {
byType = ByType.name;
} else if (type.equalsIgnoreCase("className")) {
byType = ByType.className;
}
return byType;
}
}
5.ScreenShot,儲存截圖:
package com.etyero.utils;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
public class ScreenShot {
private WebDriver driver;
public ScreenShot(WebDriver driver) {
this.driver = driver;
}
/**
* 儲存截圖
*
* @param path
* 截圖儲存路徑
* @param shotName
* 圖片命名
*
* @author ljl
*/
public void saveScreenShot(String path, String shotName) {
LogUtil log = new LogUtil(ScreenShot.class);
//TakesScreenshot介面是依賴於具體的瀏覽器API操作的,所以在HTMLUnit Driver中並不支援該操作
TakesScreenshot tScreenshot = (TakesScreenshot)driver;
// 截圖
File photo = tScreenshot.getScreenshotAs(OutputType.FILE);
File shotFile = new File(path+shotName);
try {
// 將截圖複製到指定目錄
FileUtils.copyFile(photo, shotFile);
} catch (IOException e) {
log.error(getClass() + " 儲存截圖失敗");
e.printStackTrace();
}
}
}
6.TestNGListener,監聽類,繼承TestListenerAdapter,這裡主要實現了監聽測試過程並記錄日誌,以及如果測試失敗,則儲存截圖。
package com.etyero.utils;
import org.openqa.selenium.WebDriver;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
/**
* 監聽測試過程
*
* @author ljl
*/
public class TestNGListener extends TestListenerAdapter {
private static WebDriver driver;
LogUtil log = new LogUtil(TestNGListener.class);
public static void setDriver(WebDriver driver) {
TestNGListener.driver = driver;
}
@Override
public void onTestSuccess(ITestResult tr) {
log.info("Test Success");
super.onTestSuccess(tr);
}
@Override
public void onTestFailure(ITestResult tr) {
log.error("Test Failure");
super.onTestFailure(tr);
ScreenShot screenShot = new ScreenShot(driver);
//獲取當前project目錄
String path = System.getProperty("user.dir").replace("\\", "/");
//加上時間戳以區分截圖
String curTime = TimeUtil.getDate("yyyyMMddHHmmss");
screenShot.saveScreenShot(path + "/img/", "testFail" + curTime + ".png");
}
@Override
public void onTestSkipped(ITestResult tr) {
log.error("Test Skipped");
super.onTestSkipped(tr);
}
@Override
public void onStart(ITestContext testContext) {
log.info("Test Start");
super.onStart(testContext);
}
@Override
public void onFinish(ITestContext testContext) {
log.info("Test Finish");
super.onFinish(testContext);
}
}
以上,介紹了基礎層的一些主要工具類,當然,除了這些,我們還可以封裝一些其他常用的工具類,如時間轉換,精度運算(java原生的double存在精度丟失問題),資料庫連線等,也可以在這些工具類中繼續擴充套件一些常用的方法。目的是達到程式碼的高重用性。