代码如下:
package utils;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.ITestResult;
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MultiTouchAction;
import io.appium.java_client.NoSuchContextException;
import io.appium.java_client.TouchAction;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
/**
* @author young
* @description appium api封装
* */
public class AppiumUtil {
public AppiumDriver driver;
public ITestResult it;
/**定义日志输出对象*/
public static Logger logger = Logger.getLogger(AppiumUtil.class);
/**获取driver
* @throws */
public AppiumDriver getDriver(String url,DesiredCapabilities capabilities,String platform){
if(platform.equalsIgnoreCase("android")){
try {
driver = new AndroidDriver(new URL(url), capabilities);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}else if(platform.equalsIgnoreCase("ios")){
try {
driver = new IOSDriver (new URL(url),capabilities);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}else{
}
return driver;
}
/**退出app*/
public void closeApp(String appName){
driver.closeApp();
logger.info(appName+"已经关闭");
}
/**退出移动浏览器*/
public void quit(){
driver.quit();
logger.info("driver已被清理");
}
/**通过By对象 去查找某个元素*/
public WebElement findElement(By by){
return driver.findElement(by);
}
/**
* 通过By对象 去查找一组元素
* */
public List findElements(By by) {
return driver.findElements(by);
}
/**清空元素内容*/
public void clear(By byElement){
WebElement element = findElement(byElement);
element.clear();
logger.info("清空元素:"+getLocatorByElement(element, ">")+"上的内容");
}
/**输入内容*/
public void typeContent(By byElement,String str){
WebElement element = findElement(byElement);
element.sendKeys(str);
logger.info("在元素:"+getLocatorByElement(element, ">")+"输入内容:"+str);
}
/**点击*/
public void click(By byElement){
WebElement element = findElement(byElement);
try{
element.click();
logger.info("点击元素:"+getLocatorByElement(element, ">"));
}catch(Exception e){
logger.error("点击元素:"+getLocatorByElement(element, ">")+"失败", e);
Assert.fail("点击元素:"+getLocatorByElement(element, ">")+"失败", e);
}
}
/**查找一个元素 - appium新增的查找元素方法*/
public WebElement findElement(String locateWay,String locateValue){
WebElement element = null;
switch(locateWay){
case "AccessibilityId":
element = driver.findElementByAccessibilityId(locateValue);
break;
// case "AndroidUIAutomator":
// element = driver.findElementByAndroidUIAutomator(locateValue);
// break;
case "ClassName":
element = driver.findElementByClassName(locateValue);
break;
case "CSS":
element = driver.findElementByCssSelector(locateValue);
break;
case "ID":
element = driver.findElementById(locateValue);
break;
case "LinkText":
element = driver.findElementByLinkText(locateValue);
break;
case "Name":
element = driver.findElementByName(locateValue);
break;
case "PartialLinkText":
element = driver.findElementByPartialLinkText(locateValue);
break;
case "TagName":
element = driver.findElementByTagName(locateValue);
break;
case "Xpath":
element = driver.findElementByXPath(locateValue);
break;
default:
logger.error("定位方式:"+locateWay+"不被支持");
Assert.fail("定位方式:"+locateWay+"不被支持");
}
return element;
}
/**查找一组元素 - appium新增的查找元素方法*/
public List> findElements(String locateWay,String locateValue){
List> element=null;
switch(locateWay){
case "AccessibilityId":
element = driver.findElementsByAccessibilityId(locateValue);
break;
// case "AndroidUIAutomator":
// element = driver.findElementsByAndroidUIAutomator(locateValue);
// break;
case "ClassName":
element = driver.findElementsByClassName(locateValue);
break;
case "CSS":
element = driver.findElementsByCssSelector(locateValue);
break;
case "ID":
element = driver.findElementsById(locateValue);
break;
case "LinkText":
element = driver.findElementsByLinkText(locateValue);
break;
case "Name":
element = driver.findElementsByName(locateValue);
break;
case "PartialLinkText":
element = driver.findElementsByPartialLinkText(locateValue);
break;
case "TagName":
element = driver.findElementsByTagName(locateValue);
break;
case "Xpath":
element = driver.findElementsByXPath(locateValue);
break;
default:
logger.error("定位方式:"+locateWay+"不被支持");
Assert.fail("定位方式:"+locateWay+"不被支持");
}
return element;
}
/**获取文本1*/
public String getText(By by){
return findElement(by).getText().trim();
}
/**获取文本2*/
public String getText(String locateWay,String locateValue){
String str="";
switch(locateWay){
case "AccessibilityId":
str = driver.findElementByAccessibilityId(locateValue).getText().trim();
break;
// case "AndroidUIAutomator":
// str = driver.findElementByAndroidUIAutomator(locateValue).getText().trim();
// break;
case "ClassName":
str = driver.findElementByClassName(locateValue).getText().trim();
break;
case "CSS":
str = driver.findElementByCssSelector(locateValue).getText().trim();
break;
case "ID":
str = driver.findElementById(locateValue).getText().trim();
break;
case "LinkText":
str = driver.findElementByLinkText(locateValue).getText().trim();
break;
case "Name":
str = driver.findElementByName(locateValue).getText().trim();
break;
case "PartialLinkText":
str = driver.findElementByPartialLinkText(locateValue).getText().trim();
break;
case "TagName":
str = driver.findElementByTagName(locateValue).getText().trim();
break;
case "Xpath":
str = driver.findElementByXPath(locateValue).getText().trim();
break;
default:
logger.error("定位方式:"+locateWay+"不被支持");
Assert.fail("定位方式:"+locateWay+"不被支持");
}
return str;
}
/**提交*/
public void submit(By by){
WebElement element=findElement(by);
try{
element.submit();
}catch(Exception e){
logger.error("在元素:"+getLocatorByElement(element, ">")+"做的提交操作失败",e);
Assert.fail("在元素:"+getLocatorByElement(element, ">")+"做的提交操作失败",e);
}
logger.info("在元素:"+getLocatorByElement(element, ">")+"做了提交操作");
}
/**
* 获得webview页面的标题
* */
public String getTitle() {
return driver.getTitle();
}
/**
* 获得元素 属性的文本
* */
public String getAttributeText(By elementLocator, String attribute) {
return findElement(elementLocator).getAttribute(attribute).trim();
}
/**
* 在给定的时间内去查找元素,如果没找到则超时,抛出异常
* */
public void waitForElementToLoad(int elementTimeOut, final By By) {
logger.info("开始查找元素[" + By + "]");
try {
(new WebDriverWait(driver, elementTimeOut)).until(new ExpectedCondition() {
public Boolean apply(WebDriver driver) {
WebElement element = driver.findElement(By);
return element.isDisplayed();
}
});
} catch (TimeoutException e) {
logger.error("超时!! " + elementTimeOut + " 秒之后还没找到元素 [" + By + "]");
Assert.fail("超时!! " + elementTimeOut + " 秒之后还没找到元素 [" + By + "]");
}
logger.info("找到了元素 [" + By + "]");
}
/**
* 判断文本是不是和需求要求的文本一致
* **/
public void isTextCorrect(String actual, String expected) {
try {
Assert.assertEquals(actual, expected);
} catch (AssertionError e) {
logger.error("期望的文字是 [" + expected + "] 但是找到了 [" + actual + "]");
Assert.fail("期望的文字是 [" + expected + "] 但是找到了 [" + actual + "]");
}
logger.info("找到了期望的文字: [" + expected + "]");
}
/**
* 暂停当前用例的执行,暂停的时间为:sleepTime
* */
public void pause(int sleepTime) {
if (sleepTime <= 0) {
return;
}
try {
TimeUnit.SECONDS.sleep(sleepTime);
logger.info("暂停:"+sleepTime+"秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/** 根据元素来获取此元素的定位值 */
public String getLocatorByElement(WebElement element, String expectText) {
String text = element.toString();
String expect = null;
try {
expect = text.substring(text.indexOf(expectText) + 1, text.length() - 1);
} catch (Exception e) {
e.printStackTrace();
logger.error("failed to find the string [" + expectText + "]");
}
return expect;
}
/**
* 判断实际文本时候包含期望文本
*
* @param actual
* 实际文本
* @param expect
* 期望文本
*/
public void isContains(String actual, String expect) {
try {
Assert.assertTrue(actual.contains(expect));
} catch (AssertionError e) {
logger.error("The [" + actual + "] is not contains [" + expect + "]");
Assert.fail("The [" + actual + "] is not contains [" + expect + "]");
}
logger.info("The [" + actual + "] is contains [" + expect + "]");
}
/**跳转到webview页面*/
public void switchWebview(int index){
Set contexts = driver.getContextHandles();
for (String context : contexts) {
System.out.println(context);
//打印出来看看有哪些context
}
driver.context((String) contexts.toArray()[index]);
}
/**跳转到webview页面*/
public void switchWebview(String contextName){
try{
Set contexts = driver.getContextHandles();
for (String context : contexts) {
System.out.println(context);
//打印出来看看有哪些context
}
driver.context(contextName);
}catch(NoSuchContextException nce){
logger.error("没有这个context:"+contextName, nce);
Assert.fail("没有这个context:"+contextName, nce);
}
}
/**
* 执行JavaScript 方法
* */
public void executeJS(String js) {
((JavascriptExecutor) driver).executeScript(js);
logger.info("执行JavaScript语句:[" + js + "]");
}
/**
* 执行JavaScript 方法和对象
* 用法:seleniumUtil.executeJS("arguments[0].click();", seleniumUtil.findElementBy(MyOrdersPage.MOP_TAB_ORDERCLOSE));
* */
public void executeJS(String js, Object... args) {
((JavascriptExecutor) driver).executeScript(js, args);
logger.info("执行JavaScript语句:[" + js + "]");
}
/**检查元素是不是存在*/
public boolean doesElementsExist(By byElement){
try{
findElement(byElement);
return true;
}catch(NoSuchElementException nee){
return false;
}
}
/**长按操作*/
public void longPress(By by){
TouchAction tAction=new TouchAction(driver);
tAction.longPress(findElement(by)).perform();
}
/**滑动*/
public void swipe(int beginX,int beginY,int endX,int endY){
TouchAction tAction=new TouchAction(driver);
try{
tAction.press(beginX,beginY).moveTo(endX,endY).release().perform();
}catch(Exception e){
e.printStackTrace();
}
}
/**滚动 - 根据文本模糊匹配*/
public void scroll(String text){
driver.scrollTo(text);
}
/**滚动 - 根据文本精准匹配*/
public WebElement scrollExact(String text){
return driver.scrollToExact(text);
}
/**拖拽操作*/
public void DragAndDrop(By dragElement,By dropElement){
TouchAction act=new TouchAction(driver);
act.press(findElement(dragElement)).perform();
act.moveTo(findElement(dropElement)).release().perform();
}
/**放大和缩小*/
public void zoomAndPinch(int beginX,int beginY,int endX,int endY){
int scrHeight = driver.manage().window().getSize().getHeight();
int scrWidth = driver.manage().window().getSize().getWidth();
MultiTouchAction multiTouch = new MultiTouchAction(driver);
TouchAction tAction0 = new TouchAction(driver);
TouchAction tAction1 = new TouchAction(driver);
tAction0.press(scrWidth/2,scrHeight/2).waitAction(1000).moveTo(beginX,beginY).release();
tAction1.press(scrWidth/2,scrHeight/2+40).waitAction(1000).moveTo(endX,endY).release();
multiTouch.add(tAction0).add(tAction1);
multiTouch.perform();
}
/**app置于后台运行*/
public void runBackgound(int runTimes){
driver.runAppInBackground(runTimes);
}
/**收起键盘*/
public void hideKeyboard(){
driver.hideKeyboard();
logger.info("虚拟键盘已经收起");
}
/**安装app*/
public void instalApp(String appPath){
try{
driver.installApp(appPath);
}catch(Exception e){
logger.error("app安装失败",e);
Assert.fail("app安装失败",e);
}
}
/**app是否安装*/
public boolean isAppInstalled(String appPackage){
if(driver.isAppInstalled(appPackage)){
logger.info(appPackage+":已经安装");
return true;
}else {
logger.info(appPackage+":未安装");
return false;
}
}
/**页面过长时候滑动页面 window.scrollTo(左边距,上边距); */
public void scrollPage(int x,int y){
String js ="window.scrollTo("+x+","+y+");";
((JavascriptExecutor)driver).executeScript(js);
}
}
log4j
log4j
1.2.16
provided
package com.young.appiumcombat.utils;
import java.util.Properties;
import org.apache.log4j.PropertyConfigurator;
/**
* @author young
* @decription 动态生成各个模块中的每条用例的日志,运行完成用例之后请到result/log目录下查看
* */
public class LogConfiguration {
public static void initLog(String fileName){
//获取到模块名字
String founctionName = getFunctionName(fileName);
//声明日志文件存储路径以及文件名、格式
final String logFilePath = "./result/logs/"+founctionName+"/"+fileName+".log";
Properties prop = new Properties();
//配置日志输出的格式
prop.setProperty("log4j.rootLogger","info, toConsole, toFile");
prop.setProperty("log4j.appender.file.encoding","UTF-8" );
prop.setProperty("log4j.appender.toConsole","org.apache.log4j.ConsoleAppender");
prop.setProperty("log4j.appender.toConsole.Target","System.out");
prop.setProperty("log4j.appender.toConsole.layout","org.apache.log4j.PatternLayout ");
prop.setProperty("log4j.appender.toConsole.layout.ConversionPattern","[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n");
prop.setProperty("log4j.appender.toFile", "org.apache.log4j.DailyRollingFileAppender");
prop.setProperty("log4j.appender.toFile.file", logFilePath);
prop.setProperty("log4j.appender.toFile.append", "false");
prop.setProperty("log4j.appender.toFile.Threshold", "info");
prop.setProperty("log4j.appender.toFile.layout", "org.apache.log4j.PatternLayout");
prop.setProperty("log4j.appender.toFile.layout.ConversionPattern", "[%d{yyyy-MM-dd HH:mm:ss}] [%p] %m%n");
//使配置生效
PropertyConfigurator.configure(prop);
}
/**取得模块名字*/
public static String getFunctionName(String fileName){
String functionName = null;
int firstUndelineIndex = fileName.indexOf("_");
functionName = fileName.substring(0, firstUndelineIndex-4);
return functionName;
}
}
package com.young.appiumcombat.utils;
import java.io.File;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.Assert;
import org.testng.ITestContext;
import io.appium.java_client.AppiumDriver;
/**
* @author Young
* @description 根据测试平台的不同生成不同的driver 比如AndroidDriver 或者是IOSDriver
*
* */
public class SelectDriver {
//声明driver
public AppiumDriver driver;
//声明DesiredCapabilities
public DesiredCapabilities capabilities;
//声明ITestContext,用于获取testng配置文件内容
public ITestContext testContext;
//appium server地址
public String serverURL;
//测试引擎名字
public String automationName;
//测试平台名字
public String platformName;
//测试平台版本号
public String platformVersion;
//设备名字
public String deviceName;
//ios app的路径
public String iosAppPath;
//android app路径
public String androidAppPath;
//android app的 package
public String appPackage;
//android app的activity
public String appActivity;
//安卓独有 - 是否使用unicode键盘,使用此键盘可以输入中文字符
public boolean unicodeKeyboard;
//android独有 - 是否重置键盘,如果设置了unicodeKeyboard键盘,可以将此参数设置为true,然后键盘会重置为系统默认的
public boolean resetKeyboard;
//是否覆盖已有的seesssion,这个用于多用例执行,如果不设置的话,会提示前一个session还没有结束,用例就不能继续执行了
public boolean sessionOverride;
//暂停的等待时间
public int sleepTime;
//元素等待超时时间
public int elementTimeOut;
//app文件路径,主要存储的是app的名字
public String appFilePath;
//webview的名字或者叫标识符,一般以WEBVIEW开头,例如WEBVIEW_com.microsoft.bing
public final static String WEBVIEW_NAME = null;
//原生app的名字或者标识符,一般是NATIVE_APP
public final static String NATIVEAPP_NAME = null;
//实例化本类的日志输出对象
public static Logger logger = Logger.getLogger(SelectDriver.class);
public AppiumDriver selectDriver(ITestContext context,AppiumUtil appiumUtil){
//通过testng的xml文件获取serverURL参数值,并赋给 serverURL变量
serverURL = context.getCurrentXmlTest().getParameter("serverURL");
//通过testng的xml文件获取automationName参数值,并赋给 automationName变量
automationName = context.getCurrentXmlTest().getParameter("automationName");
//通过testng的xml文件获取platformName参数值,并赋给 platformName变量
platformName = context.getCurrentXmlTest().getParameter("platformName");
//通过testng的xml文件获取platformVersion参数值,并赋给 platformVersion变量
platformVersion = context.getCurrentXmlTest().getParameter("platformVersion");
//通过testng的xml文件获取deviceName参数值,并赋给 deviceName变量
deviceName = context.getCurrentXmlTest().getParameter("deviceName");
//通过testng的xml文件获取androidAppPath参数值,并赋给 androidAppPath变量
androidAppPath = context.getCurrentXmlTest().getParameter("androidAppPath");
//通过testng的xml文件获取iosAppPath参数值,并赋给 iosAppPath变量
iosAppPath = context.getCurrentXmlTest().getParameter("iosAppPath");
//通过testng的xml文件获取appPackage参数值,并赋给 appPackage变量
appPackage = context.getCurrentXmlTest().getParameter("appPackage");
//通过testng的xml文件获取appActivity参数值,并赋给 appActivity变量
appActivity = context.getCurrentXmlTest().getParameter("appActivity");
//通过testng的xml文件获取unicodeKeyboard参数值,并赋给 unicodeKeyboard变量
unicodeKeyboard = Boolean.parseBoolean(context.getCurrentXmlTest().getParameter("unicodeKeyboard"));
//通过testng的xml文件获取resetKeyboard参数值,并赋给 resetKeyboard变量
resetKeyboard = Boolean.parseBoolean(context.getCurrentXmlTest().getParameter("resetKeyboard"));
//通过testng的xml文件获取sessionOverride参数值,并赋给 sessionOverride变量
sessionOverride = Boolean.parseBoolean(context.getCurrentXmlTest().getParameter("sessionOverride"));
//通过testng的xml文件获取sleepTime参数值,并赋给 sleepTime变量
sleepTime = Integer.valueOf(context.getCurrentXmlTest().getParameter("sleepTime"));
//通过testng的xml文件获取elementTimeOut参数值,并赋给 elementTimeOut变量
elementTimeOut = Integer.valueOf(context.getCurrentXmlTest().getParameter("elementTimeOut"));
//通过testng的xml文件获取appFilePath参数值,并赋给 appFilePath变量
appFilePath = context.getCurrentXmlTest().getParameter("appFilePath");
this.testContext = context;
capabilities = new DesiredCapabilities();
//告诉测试程序,当前项目目录在哪里
File classpathRoot = new File(System.getProperty("user.dir"));
//设置capability,以便和appium创建session
capabilities.setCapability("platformName",platformName);
capabilities.setCapability("platformVersion",platformVersion);
capabilities.setCapability("deviceName",deviceName);
capabilities.setCapability("sessionOverride", sessionOverride);
//如果测试平台是android的话,执行下面这个if语句内容
if(platformName.equalsIgnoreCase("android")){
/**
* 设置和android 测试相关的capability并实例化driver对象
* */
File app = new File(classpathRoot, androidAppPath);
capabilities.setCapability("app", app.getAbsolutePath());
capabilities.setCapability("unicodeKeyboard", unicodeKeyboard);
capabilities.setCapability("resetKeyboard", resetKeyboard);
capabilities.setCapability("automationName",automationName);
capabilities.setCapability("appPackage", appPackage);
capabilities.setCapability("appActivity", appActivity);
driver = appiumUtil.getDriver(serverURL, capabilities,platformName);
testContext.setAttribute("APPIUM_DRIVER", driver);
logger.info(PropertiesDataProvider.getTestData(appFilePath, appPackage)+"已经启动");
driver.manage().timeouts().implicitlyWait(elementTimeOut, TimeUnit.SECONDS);
return driver;
//如果测试平台是ios的话,执行下面这个if语句内容
}else if(platformName.equalsIgnoreCase("ios")){
/**
* 设置和ios 测试相关的capability并实例化driver对象
* */
File app = new File(classpathRoot, iosAppPath);
capabilities.setCapability("app", app.getAbsolutePath());
//ios设置自动接收系统alert,注意IOS弹出的alert,APPIUM可以自动处理掉,支持ios8以上系统
capabilities.setCapability("autoAcceptAlerts", true);
driver = appiumUtil.getDriver(serverURL, capabilities,platformName);
testContext.setAttribute("APPIUM_DRIVER", driver);
driver.manage().timeouts().implicitlyWait(elementTimeOut,TimeUnit.SECONDS);
}else{
logger.error("初始化driver失败");
Assert.fail("初始化driver失败");
}
//最后返回dirver对象
return driver;
}
}
package com.young.appiumcombat.utils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
/**
* @author young
* @Desription 从.properties文件中读取相关测试数据
*
* */
public class PropertiesDataProvider {
public static String getTestData(String configFilePath, String key) {
Configuration config = null;
try {
config = new PropertiesConfiguration(configFilePath);
} catch (ConfigurationException e) {
e.printStackTrace();
}
return String.valueOf(config.getProperty(key));
}
}
哈,我们使用https://blog.csdn.net/A_Kaka/article/details/106988528这里的arrow的报告插件,在将CONFIGFILE值修改成res/properties目录下的 config.properties,代码如下:
1 retrycount=0
2 sourcecodedir=com/young/appiumcombat/testcases
3 sourcecodeencoding=UTF-8
另:笔者建立了一个软件测试技术交流群1125760266,欢迎大家加入进来一起学习,探讨