1. 程式人生 > >webUI自動化測試框架(二):程式碼分層-基礎層

webUI自動化測試框架(二):程式碼分層-基礎層

前言:該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);
	}

}
以上,介紹了基礎層的一些主要工具類,當然,除了這些,我們還可以封裝一些其他常用的工具類,如時間轉換,精度運算(java原生的double存在精度丟失問題),資料庫連線等,也可以在這些工具類中繼續擴充套件一些常用的方法。目的是達到程式碼的高重用性。