Selenium封装
在封装过程中我尽量做到2点:多次出现的代码尽量提取出来;结构清晰保证可阅读性。下面是我规划了一个相对明了的结构体系,可以帮助自己比较好的构建好整个封装思路。
在这个封装过程中会发现我们在元素查找仅到By,这是因为为了更好的解决由于页面元素变化增加自动化维护成本的问题,我在下面的文章中会对页面元素封装思路中有具体的介绍。
我们在开始方法之前我们先做一个准备工作,我们需要测试的相关浏览器包含哪些,我这里先选择了四种:chrome,opera,gecko,ie。那么我们针对四个浏览器准备一下驱动文件:在目录:StudySelenium\res 下放入相关的驱动文件。之后我们做一下准备工作:
1.将驱动的先关路径及驱动名保存在properties
BrowserCoreType=1
# Drivers
ChromeDriver =webdriver.chrome.driver
OperaDriver=webdriver.opera.driver
GeckoDriver=webdriver.gecko.driver
IEDriver =webdriver.ie.driver
# Drivers' path
ChromeDriverPath =res/chromedriver.exe
OperaDriverPath=res/operadriver.exe
GeckoDriverPath=res/geckodriver.exe
IEDriverPath = res/IEDriverServer.exe
# UI actions' interval in millisecond
StepInterval = 500
# UI actions' timeout in millisecond
Timeout = 30000
# UI actions' timepass in millisecond
TimePass = 1000
并在 GlobalSettings.java 中进行一个调用
/**
* SeleniumKing webdriver
*/
public static String chromeDriver = prop.getProperty("ChromeDriver");
public static String ieDriver = prop.getProperty("IEDriver");
public static String operaDriver = prop.getProperty("OperaDriver");
public static String geckoDriver = prop.getProperty("GeckoDriver");
public static String chromeDriverPath = prop.getProperty("ChromeDriverPath");
public static String ieDriverPath = prop.getProperty("IEDriverPath");
public static String operaDriverPath = prop.getProperty("OperaDriverPath");
public static String geckoDriverPath = prop.getProperty("GeckoDriverPath");
public static int browserCoreType = Integer.parseInt(prop.getProperty("BrowserCoreType"));
public static String stepInterval = prop.getProperty("StepInterval");
public static String timeout = prop.getProperty("Timeout");
public static String timePass = prop.getProperty("TimePass");
public static String baseStorageUrl = prop.getProperty("baseStorageUrl", System.getProperty("user.dir"));
2.在SeleniumKing.java中将准备工作做好
public WebDriver driver;
private Logger logger = Logger.getLogger(SeleniumKing.class.getName());
public SeleniumKing() {
Properties props = System.getProperties(); // 获得系统属性集
String currentPlatform = props.getProperty("os.name"); // 操作系统名称 logger.info("当前操作系统是:[" + currentPlatform + "]");
if (currentPlatform.toLowerCase().contains("win")) {
//如果是windows平台
switch (GlobalSettings.browserCoreType) {
//1:chrome
case 1:
System.setProperty(GlobalSettings.chromeDriver, GlobalSettings.chromeDriverPath); ChromeOptions option = new ChromeOptions(); option.addArguments("disable-infobars");
driver = new ChromeDriver(option); driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS); logger.info("启动测试浏览器:[" + GlobalSettings.chromeDriver + "]");
break;
//2:opera
case 2:
System.setProperty(GlobalSettings.operaDriver, GlobalSettings.operaDriverPath); driver = new OperaDriver();
logger.info("启动测试浏览器:[" + GlobalSettings.operaDriver + "]");
break;
//3:firefox
case 3:
System.setProperty(GlobalSettings.geckoDriver, GlobalSettings.geckoDriverPath); driver = new FirefoxDriver();
logger.info("启动测试浏览器:[" + GlobalSettings.geckoDriver + "]");
break;
//4:ie
case 4:
System.setProperty(GlobalSettings.ieDriver, GlobalSettings.ieDriverPath); driver = new InternetExplorerDriver();
logger.info("启动测试浏览器:[" + GlobalSettings.ieDriver + "]");
break; }
driver.manage().deleteAllCookies();
}else if (currentPlatform.toLowerCase().contains("linux")) {
//如果是linux平台
logger.error("暂不支持当前操作系统:[" + currentPlatform + "]");
Assert.fail("暂不支持当前操作系统:[" + currentPlatform + "]");
} else if (currentPlatform.toLowerCase().contains("mac")) {
//如果是mac平台
logger.error("暂不支持当前操作系统:[" + currentPlatform + "]");
Assert.fail("暂不支持当前操作系统:[" + currentPlatform + "]"); }
else{
logger.error("暂不支持当前操作系统:[" + currentPlatform + "]");
Assert.fail("暂不支持当前操作系统:[" + currentPlatform + "]"); } }
这里需要说明的两点:1,是为了以后我们能兼容更多的平台,我们做了个平台的判断;2.我们需要做个info等级的记录。说道记录我们还需要做一个准备工作就是截图功能,能够在出现异常的时候进行一个异常记录。
private String screenShot() {
String dir = "target/screenshot"; // TODO
String time = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()); String screenShotPath = dir + File.separator + time + ".png";
try {
File sourceFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); FileUtils.copyFile(sourceFile, new File(screenShotPath));
} catch (Exception e) {
e.printStackTrace();
return "Failed to screenshot";
}
return screenShotPath.replace("\\", "/");
}
这个截图功能我们是返回了一个截图信息,在不同的使用情况下我们可以做不同的处理。
之后我们需要进行整理的就是操作类,辅助类,断言类。这里操作类主要是对一些控件或者必要元素的操作比如:点击,输入还有就是常用到的frame的操作;辅助类主要是来获取元素及控件的相关属性比如:文本内容,勾选状态等;断言类主要是对预期以及实际效果的一个对比比如:文本一致,文本包含,大小对比。
会发现在进行操作前我们是需要获取到WebElement对象前面也有提到过这里我们选择将元素封装成BY 来进行查找,主要是为了保障我们在UI元素在新的版本中发生的情况下,尽可能的减少我们的一个维护工作量,其实在以前我使用的是将元素的某一个元素属性已String的形式保存下来,之后在用例中通过不同的方法进行一个获取及操作。但是在实际的项目中发现这样的封装效果其实很差。其实在UI改版的过程中好多属性都会失效,导致我们需要其它属性进行一个定位。还是需要在case级别进行一个修改。那么现在我们只需要将这个元素获取的方式进行封装,那么只要这个入口控件是存在的我们都只需要进行一处的修改就可以了。同时在定位过程中我们发现元素定位是一个比较快的过程,但是元素在加载过程中可能比较慢,导致我们在发起定位时元素还没有加载出来,导致用例执行失败,稳定性极差。那么我们需要做一个延时查找到动作,在一定时间范围内,我们每隔一定时间进行一个查找,如果查到了我们进行返回,如果没有查到我们就在等一段时间后再次进行查找。如果超出规定时间还是没有找打元素,我们直接报错并记录日志及先关截图工作供以后复查的一个参考。下面是相关代码:
private WebElement findElement(By by){
final long endTime = System.currentTimeMillis()+timeout;
WebElement element = null;
while (true) {
try { element =driver.findElement(by);
if (element.isDisplayed()) {
break; }
} catch (Exception e) {
if (System.currentTimeMillis() < endTime) {
try { Thread.sleep(500);
} catch (InterruptedException e1) { e1.printStackTrace();
}
} else {
handleFailure("相关元素未找到"+by);
break; } } }
try { Thread.sleep(timePass);
} catch (InterruptedException e) {
e.printStackTrace(); }
return element;
}
这里我们需要对未找到元素的截图做一个简单的封装,也可以在之后操作异常中能够使用,我们在调用过程中需要给到一个截图原因方便我们对代码的维护。
private void handleFailure(String notice) {
String png = LogTools.screenShot(this);
String log = "【"+notice + "】捕捉屏幕截图>> " + png;
logger.error(log);
Assert.fail(log); }
下面是整体需要进行实现的一些方法:
由于代码量比较多,我进做几个举例:
/** * 打开浏览器 * @param url 浏览器地址 */
public void openUrl(String url) {
driver.get(url);
logger.info("打开地址【"+url+"】"); }
/** * 退出浏览器 */
public void quit() {
driver.quit();
logger.info("退出浏览器"); }
/** * 点击 *@param by 界面元素 */
public void click(By by) {
WebElement webElement =findElement(by);
webElement.click();
logger.info("点击;【"+by+"】"); }
/** * 获得页面的标题 * */
public String getTitle() {
return driver.getTitle(); }
/** * 获得元素的文本 * */
public String getText(By elementLocator) {
return driver.findElement(elementLocator).getText(); }
/** * 包含断言 * @param text1 包含 * @param text2 被包含 */
public void assertStringContain(String text1,String text2){
if (text1.contains(text2)) {
logger.info("包含断言成功 : 【"+text1+"】"+" 包含 【"+text2+"】");
Assert.assertTrue(true); } else {
handleFailure("包含断言失败 : 【"+text1+"】"+" 不包含 【"+text2+"】"); } }
到这里我们的SeleniumKing类基本上就封装完了,在以后的工作中我们可以跟具不同的项目需要进行添加或者修改。