博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UI层自动化测试框架(三):基础层
阅读量:4229 次
发布时间:2019-05-26

本文共 14688 字,大约阅读时间需要 48 分钟。

引言

第一章我就有说到,该自动化测试框架分为四层:基础层,对象层,操作层,用例层。本章主要介绍基础层的设计。

测试框架目录如图:

这里写图片描述

对应源码地址:

基础层介绍

该层封装Appium的相关操作和一些常见的工具类。

为什么要单独搞一个基础层呢?

这样的目的还是为了体现Java的核心思想:封装,这样框架显得更加的清晰,简练,代码的复用性和拓展性更高。每一层负责各自的功能,比如:我在基础层专门写一个读取xml数据的工具类,这样,其他层如果想要解析xml格式的数据时,只要调用基础层的这个封装好的XmlUtils工具类即可,这样是不是很方便。

基础层常见的类

接下来主要讲解基础层的各个类的作用和实现原理

如图为基础层核心的类:
这里写图片描述

1. AppiumExecutor

AppiumExecutor为一个接口,包含若干个抽象方法,这些方法都是appium基本的操作方法,比如:点击,输入,获取文本等等等

这里写图片描述

package com.dji.utils;import com.dji.object.Locator;import io.appium.java_client.MobileElement;/** * Appium常见的API *  * @author Charlie.chen * */public interface AppiumExecutor {
//点击元素 public void click(Locator locator); //输入文本 public void type(Locator locator, String values); //获取元素文本 public String getText(Locator locator); //获取元素 public MobileElement findElement( Locator locator) ; //判断元素是否出现 public boolean isElementDisplayed(Locator locator); //向左滑动 public void swipeToLeft(); //向右滑动 public void swipeToRight(); //向上滑动 public void swipeToUp(); //向下滑动 public void swipeToDown(); //通过坐标点击 public void tapByXY(int x, int y);}

2. AppiumExecutorImpl

封装一个AppiumExecutorImpl类,实现AppiumExecutor接口

这里写图片描述

package com.dji.utils;import java.io.IOException;import org.openqa.selenium.By;import com.dji.object.Locator;import io.appium.java_client.AppiumDriver;import io.appium.java_client.MobileElement;import io.appium.java_client.TouchAction;/** * 封装一个AppiumExecutorImpl类,实现AppiumExecutor接口 *  * @author Charlie.chen * */public class AppiumExecutorImpl implements AppiumExecutor {
private AppiumDriver
driver; public Log log=new Log(this.getClass()); public AppiumExecutorImpl(AppiumDriver
driver) { this.driver = driver; } public AppiumDriver
getDriver() { return driver; } public void setDriver(AppiumDriver
driver) { this.driver = driver; } /** * 在元素中中输入文本 * * @author Charlie.chen * @param locator * @param values * @throws Exception */ public void type(Locator locator, String values){ MobileElement e = (MobileElement) findElement(locator); e.sendKeys(values); } /** * 点击元素 * * @author Charlie.chen * @param locator * @throws Exception */ public void click(Locator locator) { MobileElement e = (MobileElement) findElement(locator); e.click(); } /** * 获取元素文本信息 * * @author Charlie.chen * @param locator */ public String getText(Locator locator) { // TODO Auto-generated method stub MobileElement e = (MobileElement) findElement(locator); String text=e.getText(); return text; } /** * 获取元素 * * @author Charlie.chen * @param locator * @return */ public MobileElement findElement(Locator locator) { MobileElement e=null; switch (locator.getBy()) { case xpath: e = (MobileElement) driver.findElement(By.xpath(locator.getAddress())); break; case id: e = (MobileElement) driver.findElement(By.id(locator.getAddress())); break; case name: e = (MobileElement) driver.findElement(By.name(locator.getAddress())); break; case className: e = (MobileElement) driver.findElement(By.className(locator.getAddress())); break; default: e = (MobileElement) driver.findElement(By.id(locator.getAddress())); } return e; } /** * 向左滑动 */ public void swipeToLeft() { int x = driver.manage().window().getSize().width; int y = driver.manage().window().getSize().height; try { driver.swipe((x / 8 * 7), (y / 2 * 1), (x / 8 * 1), (y / 2 * 1), 1000); } catch (Exception e) { driver.swipe((x / 8 * 7), (y / 2 * 1), (x / 8 * 1), (y / 2 * 1), 1000); } } /** * 向右滑动 */ public void swipeToRight() { int x = driver.manage().window().getSize().width; int y = driver.manage().window().getSize().height; try { driver.swipe((x / 8 * 1), (y / 2 * 1), (x / 8 * 7), (y / 2 * 1), 1000); } catch (Exception e) { driver.swipe((x / 8 * 1), (y / 2 * 1), (x / 8 * 7), (y / 2 * 1), 1000); } } /** * 向上滑动 */ public void swipeToUp() { int x = driver.manage().window().getSize().width; int y = driver.manage().window().getSize().height; try { driver.swipe((x / 2 * 1), (y / 4 * 3), (x / 2 * 1), (y / 4 * 1), 1500); } catch (Exception e) { driver.swipe((x / 2 * 1), (y / 4 * 3), (x / 2 * 1), (y / 4 * 1), 1500); } } /** * 向下滑动 */ public void swipeToDown() { int x = driver.manage().window().getSize().width; int y = driver.manage().window().getSize().height; try { driver.swipe((x / 2 * 1), (y / 8 * 1), (x / 2 * 1), (y / 8 * 7), 1000); } catch (Exception e) { driver.swipe((x / 2 * 1), (y / 8 * 1), (x / 2 * 1), (y / 8 * 7), 1000); } } /** * 通过坐标点击 */ public void tapByXY(int x, int y) { TouchAction to = new TouchAction(driver); try { to.tap(x, y).release().perform(); } catch (Exception e) { System.out.println("点击失败"); } } /** * 判断元素是否出现 * * @author Charlie.chen * @param locator * @param timeOut * @return * @throws IOException */ public boolean isElementDisplayed( Locator locator) { boolean flag = false; try { findElement(locator).isDisplayed(); flag = true; } catch (Exception e) { flag = false; } return flag; }}

3. DriverFactory

封装一个DriverFactory类,便于创建基于Android和iOS的driver,当创建Android的driver时,只用调用createAndroidDriver方法即可。

这里写图片描述

package com.dji.utils;import java.net.MalformedURLException;import java.net.URL;import org.openqa.selenium.By;import org.openqa.selenium.remote.DesiredCapabilities;import io.appium.java_client.android.AndroidDriver;import io.appium.java_client.ios.IOSDriver;/** * 创建基于Android和iOS的driver *  * @author Charlie.chen * */public class DriverFactory {
private static AndroidDriver
androidDriver = null; private static IOSDriver
iosDriver = null; private static Log log = new Log(DriverFactory.class); @SuppressWarnings("rawtypes") public static AndroidDriver
createAndroidDriver(String udid, String port, String appPackage, String appActivity) { DesiredCapabilities caps = new DesiredCapabilities(); // apk地址,不需要安装的话这行不需要 // File app=new File("C:\\Users\\charlie.chen\\djigo.apk"); // 不需要安装的话就去掉这个 // caps.setCapability("app", app.getAbsolutePath()); caps.setCapability("platformName", "Android"); caps.setCapability("platformVersion", "6.0"); caps.setCapability("deviceName", "P9"); caps.setCapability("udid", udid); caps.setCapability("appPackage", appPackage); caps.setCapability("appActivity", appActivity); // caps.setCapability(MobileCapabilityType.BROWSER_NAME, "Chrome"); caps.setCapability("unicodeKeyboard", "True"); // 支持中文输入 caps.setCapability("resetKeyboard", "True"); // 重置输入法为系统默认 // 安装时不对apk进行重签名,设置很有必要,否则有的apk在重签名后无法正常使用 // caps.setCapability("noSign", "True"); try { androidDriver = new AndroidDriver(new URL("http://127.0.0.1:" + port + "/wd/hub"), caps); } catch (Exception e) { log.error("Android.appium连接失败"); } return androidDriver; } @SuppressWarnings("rawtypes") public static IOSDriver
createIOSDriver(String udid, String port) { DesiredCapabilities caps = new DesiredCapabilities(); caps.setCapability("platformName", "iOS"); caps.setCapability("platformVersion", "9.3"); caps.setCapability("deviceName", "iPhone 6s"); caps.setCapability("unicodeKeyboard", "True"); caps.setCapability("resetKeyboard", "True"); try { iosDriver = new IOSDriver(new URL("http://127.0.0.1:" + port + "/wd/hub"), caps); } catch (MalformedURLException e) { log.error("iOS.appium连接失败"); } return iosDriver; }}

4. XmlUtils

XmlUtils工具类负责读取xml格式的文件,例如page.xml就是通过XmlUtils进行读取解析的(page.xml文件在下一章会讲到),这里用到了第三方的开源框架dom4j

package com.dji.utils;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.util.HashMap;import java.util.Iterator;import org.dom4j.Attribute;import org.dom4j.Document;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.dom4j.io.OutputFormat;import org.dom4j.io.SAXReader;import org.dom4j.io.XMLWriter;import com.dji.object.Locator;import com.dji.object.Locator.ByType;/** *  将元素放在page.xml统一管理,要获取元素的信息,通过pageName从xml文件中读取。 *  读取xml的页面元素是使用dom4j开源框架 *   * @author Charlie.chen * */public class XmlUtils {    public static HashMap
readXMLDocument(String xmlPath,String pageName) throws Exception { Log log = new Log(XmlUtils.class); HashMap
locatorMap = new HashMap
(); locatorMap.clear(); File file = new File(xmlPath); if (!file.exists()) { log.error("Can't find " + xmlPath); return locatorMap=null; } //创建SAXReader对象 SAXReader reader = new SAXReader(); //读取文件 转换成Document Document document = reader.read(file); //获取根节点元素对象 Element root = document.getRootElement(); //遍历 for (Iterator
i = root.elementIterator(); i.hasNext();) {
Element page = (Element) i.next(); if (page.attribute(0).getValue().equalsIgnoreCase(pageName)) { log.info("pageName is:" + pageName); for (Iterator
l = page.elementIterator(); l.hasNext();) {
String type = null; String timeOut = "3"; String value = null; String locatorName = null; Element locator = (Element) l.next(); for (Iterator
j = locator.attributeIterator(); j.hasNext();) {
Attribute attribute = (Attribute) j.next(); if (attribute.getName().equals("type")) { type = attribute.getValue(); //log.info("get locator type " + type); } else if (attribute.getName().equals("timeOut")) { timeOut = attribute.getValue(); //log.info("get locator timeOut " + timeOut); } else { value = attribute.getValue(); //log.info("get locator value " + value); } } Locator temp = new Locator(value,Integer.parseInt(timeOut), getByType(type)); locatorName = locator.getText(); //log.info("locatorName is " + locatorName); locatorMap.put(locatorName, temp); } continue; } } return locatorMap; } 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

截图并保存到本地,其中包括两个主要的方法,getCurrentTime获取当前时间,getScreenShot进行截图

package com.dji.utils;import java.io.File;import java.text.SimpleDateFormat;import java.util.Date;import org.apache.commons.io.FileUtils;import org.openqa.selenium.OutputType;import io.appium.java_client.AppiumDriver;/** * 截图并保存至本地 *  * @author Charlie.chen */public class ScreenShot {
private AppiumDriver
driver; // 测试失败截屏保存的路径 private String path; public Log log=new Log(this.getClass()); public ScreenShot(AppiumDriver
driver){ this.driver=driver; path=System.getProperty("user.dir")+ "//snapshot//"+ this.getClass().getSimpleName()+"_"+getCurrentTime() + ".png"; } public void getScreenShot() { File screen = driver.getScreenshotAs(OutputType.FILE); File screenFile = new File(path); try { FileUtils.copyFile(screen, screenFile); log.info("截图保存的路径:" + path); } catch (Exception e) { log.error("截图失败"); e.printStackTrace(); } } /** * 获取当前时间 */ public String getCurrentTime(){ Date date=new Date(); SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH-mm-ss"); String currentTime=sdf.format(date); return currentTime; } public String getPath() { return path; } public void setPath(String path) { this.path = path; }}

6. TestNGListener

TestNGListener继承TestListenerAdapter,当 testNG执行case 失败后 ,testNGListener会捕获执行失败,如果要实现失败自动截图,重写Listener的onTestFailure方法

package com.dji.utils;import org.testng.ITestContext;import org.testng.ITestResult;import org.testng.TestListenerAdapter;import io.appium.java_client.AppiumDriver;/** * testNG执行case 失败后 ,testNG Listener会捕获执行失败 * 如果要实现失败自动截图,需要重写Listener的onTestFailure方法 *  * @author Charlie.chen */public class TestNGListener extends TestListenerAdapter {
private static AppiumDriver
driver; Log log = new Log(this.getClass()); public static void setDriver(AppiumDriver
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); screenShot.getScreenShot(); } @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); }}

总结

到此基础层的各个核心的类就讲解完成了,你可以把它理解成工具层,就是你需要什么工具,直接通过基础层来调用。当然,在你的实际框架中还可以继续拓展基础层,比如,如果你的数据是存储在数据库中,这时,你就可以封装一个读取数据库,依赖于JDBC的工具类,放在基础层,然后如果其他层需要读取数据库中的数据,就可以直接调用了。

下一章将讲解 自动化测试框架(四):对象层

最后再贴一下项目源码:

你可能感兴趣的文章
Mysql多表查询语句,授权用户与密码更改
查看>>
MySQL 备份与恢复
查看>>
函数可重入性及编写规范
查看>>
想成为嵌入式程序员应知道的0x10个基本问题
查看>>
可重入函数与不可重入函数
查看>>
关于预处理器的学习
查看>>
Windows CE下操作GPIO的方法(以ARM9 S3C2410为例)
查看>>
s3c2410物理地址和虚拟地址空间
查看>>
VC中常用数据类型转换
查看>>
VC中常用类型转换2
查看>>
windows变量前缀总结(转载)
查看>>
UNICODE编程
查看>>
LPTSTR、LPCSTR、LPCTSTR、LPSTR的意义
查看>>
Visual C++中的ODBC编程
查看>>
AD590的引脚使用
查看>>
VC++工程文件下的各个文件说明
查看>>
VC中常用调试技巧
查看>>
WINCE的内存配置
查看>>
如何通过命令行方式修改XWindows的分辨率和刷新频率
查看>>
qtopia 4.2.3 移植 交叉编译记录&总结
查看>>