前言:該webUI自動化框架主要分爲四層:基礎層、對象層、操作層、用例層,每一層負責各自的功能,這樣有益於提高代碼的可讀性,複用性和擴展性。基礎層主要封裝了一些工具類,如解析xml文件,讀取excel,分瀏覽器啓動,時間處理等,供其他類調用。
另外,筆者這邊主要使用的第三方庫有:
TestNG:負責斷言、測試腳本的管理以及輸出測試報告,安裝及使用教程見筆者的另一篇博客:http://blog.csdn.net/u010798968/article/details/73549612
log4j:負責生成日誌
dom4j:解析xml
代碼大致結構如下圖所示:
進入正題,介紹基礎層各個核心類。
1.UIExecutor爲一個接口,包含了若干個抽象方法,這些方法都是webdriver中常用的操作方法,如點擊,獲取文本,切換窗口等,後續有需要擴展的頁面操作都可以在該接口中定義。
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);
}
2.UIExecutor接口的實現類UIExecutorImpl:
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);
}
}