天天看点

webUI自动化测试框架(二):代码分层-基础层

前言:该webUI自动化框架主要分为四层:基础层、对象层、操作层、用例层,每一层负责各自的功能,这样有益于提高代码的可读性,复用性和扩展性。基础层主要封装了一些工具类,如解析xml文件,读取excel,分浏览器启动,时间处理等,供其他类调用。

另外,笔者这边主要使用的第三方库有:

TestNG:负责断言、测试脚本的管理以及输出测试报告,安装及使用教程见笔者的另一篇博客:http://blog.csdn.net/u010798968/article/details/73549612

log4j:负责生成日志

dom4j:解析xml

代码大致结构如下图所示:

webUI自动化测试框架(二):代码分层-基础层

进入正题,介绍基础层各个核心类。

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存在精度丢失问题),数据库连接等,也可以在这些工具类中继续扩展一些常用的方法。目的是达到代码的高重用性。

继续阅读