Appium自动化测试框架
1.在utils包中创建一个AppiumUtil类,这个类是对appium api进行封装的。
代码如下:
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<WebElement> driver;
public ITestResult it;
/**定义日志输出对象*/
public static Logger logger = Logger.getLogger(AppiumUtil.class); /**获取driver
* @throws */
public AppiumDriver<WebElement> getDriver(String url,DesiredCapabilities capabilities,String platform){ if(platform.equalsIgnoreCase("android")){
try {
driver = new AndroidDriver<WebElement>(new URL(url), capabilities);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}else if(platform.equalsIgnoreCase("ios")){
try {
driver = new IOSDriver<WebElement> (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<WebElement> 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<Boolean>() { 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<String> 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<String> 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);
} }
2.因为要显示log4日志,在pom.xml中加入jar包类:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>provided</scope>
</dependency>
3.加载完成后要创建一个处理log类,名为:LogConfiguration类,代码如下:
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; } }
4.因为我们要处理android和ios的项目,更方便处理兼容,在Utils中创建一个SelectDriver类,代码如下:
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<WebElement> 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<WebElement> 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; } }
5.有时候我们不想把太多的配置放到testng.xml中,我们就需要根据key读取属性文件里面的value值,现在res/下创建properties,里面有app.properties 和config.properties 两个文本,然后在utils下写一个方法获取value的值,类为:PropertiesDataProvider,代码如下:
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文件中读取相关测试数据<br>
*
* */
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)); }
}
6.html报告插件
哈,我们使用http://www.cnblogs.com/sunny-sl/p/7451582.html这里的arrow的报告插件,在将CONFIGFILE值修改成res/properties目录下的 config.properties,代码如下:
retrycount=0
sourcecodedir=com/young/appiumcombat/testcases
sourcecodeencoding=UTF-8
Appium自动化测试框架的更多相关文章
- Appium+python自动化(三十八) - Appium自动化测试框架综合实践 - 框架简介-助你冲击高薪,迎娶白富美(超详解)
简介 好久没有更新博客了,博友们是不是有点等不及了.不好意思啊,中秋节过后太忙了,这篇是好不容易抽点零碎时间写的.从这一篇开始小伙伴或者童鞋们,就跟随宏哥的脚步,一步步的从无到有,从0到1的搭建一个完 ...
- Appium+python自动化(三十九)-Appium自动化测试框架综合实践 - 代码实现(超详解)
简介 经过一段时间的准备,完善的差不多了,继续分享有关Appium自动化测试框架综合实践.想必小伙伴们有点等不及了吧! driver配置封装 kyb_caps.yaml 配置表 参考代码 platfo ...
- Appium+python自动化(四十)-Appium自动化测试框架综合实践 - 代码实现(超详解)
1.简介 今天我们紧接着上一篇继续分享Appium自动化测试框架综合实践 - 代码实现.由于时间的关系,宏哥这里用代码给小伙伴演示两个模块:注册和登录. 2.业务模块封装 因为现在各种APP的层出不群 ...
- Appium+python自动化(四十一)-Appium自动化测试框架综合实践 - 即将落下帷幕(超详解)
1.简介 今天我们紧接着上一篇继续分享Appium自动化测试框架综合实践 - 代码实现.到今天为止,大功即将告成:框架所需要的代码实现都基本完成. 2.data数据封装 2.1使用背景 在实际项目过程 ...
- Appium+python自动化(四十二)-Appium自动化测试框架综合实践- 寿终正寝完结篇(超详解)
1.简介 按照上一篇的计划,今天给小伙伴们分享执行测试用例,生成测试报告,以及自动化平台.今天这篇分享讲解完.Appium自动化测试框架就要告一段落了. 2.执行测试用例&报告生成 测试报告, ...
- 『与善仁』Appium基础 — 8、Appium自动化测试框架介绍
目录 1.主流的移动端自动化测试框架 (1)Robotium (2)Macaca (3)Appium 2.自动化测试工具的选择 3.Appium简介 提示:我们前面说的Android环境搭建和adb命 ...
- Appium 自动化测试框架:关键字驱动+数据驱动
1. 关键字驱动框架简介 2. 框架结构说明 3. 框架代码实现 action 包 page_action.py business_process 包 case_process.py data_so ...
- 基于C#的Appium自动化测试框架(Ⅰ)
因为工作原因,使用的编程语言都是C#,但是国内相应的Appium资料少得可怜,Java版本的Appium也考虑过,但是奈何自己搞不定Eclipse这个编译环境[说白了就是因为懒-- 无意中看到了外面的 ...
- 基于C#的Appium自动化测试框架(Ⅰ):程序结构
因为工作原因,使用的编程语言都是C#,但是国内相应的Appium资料少得可怜,Java版本的Appium也考虑过,但是奈何自己搞不定Eclipse这个编译环境[说白了就是因为懒…… 无意中看到了外面的 ...
- Windows系统下搭建Appium自动化测试框架
简介 一种开源的测试框架(http://appium.io/) 能够用来测试原生Android/iOS应用.混合应用以及webapp 通过webdriver协议来操作应用,其核心是一个web服务器,接 ...
随机推荐
- 20145226夏艺华 《Java程序设计》第7&8周学习总结、实验一
[实验一]http://www.cnblogs.com/bestixyh/p/6358734.html [第7周]http://www.cnblogs.com/bestixyh/p/6380475.h ...
- 【CF833D】Red-Black Cobweb
[CF833D]Red-Black Cobweb 题面 洛谷 题解 看到这种统计路径的题目当然是淀粉质啦. 考虑转化一下信息设一条路径上有红点\(a\)个,黑点\(b\)个 则\(2min(a,b)\ ...
- 我们一起学习WCF 第七篇会话模式
会话:就是客户端和服务端之间的谈话.比喻A和B去登陆网站,那么A用户登陆进去肯定显示A的用户详情,那么这就是A和服务器之间的交流.同样B用户登陆之后显示B的详情,这就表示这是B和服务器之间的交流. 如 ...
- css控制字体线使用:text-decoration
css控制字体下划线使用text-decoration : text-decoration:none 无装饰,通常对html下划线标签去掉下划线样式 text-decoration:underline ...
- php常用的几个预定义变量
__FILE__:返回所在路径文件名和文件名称 __DIR__:返回文件所在的完整目录 __LINE__:返回当前文件代码的行号 __CLASS__:返回当前类名 __FUNCTION__:返回当前方 ...
- HTTP结构讲解——《HTTP权威指南》系列
HTTP结构 第二部分的5章主要介绍了HTTP服务器,代理,缓存,网关和机器人应用程序,这些都是Web系统架构的构造模块. Web服务器 第五章 Web服务器会对HTTP请求进行处理并提供响应.术语& ...
- 人脸检测及识别python实现系列(6)——终篇:从实时视频流识别出“我”
人脸检测及识别python实现系列(6)——终篇:从实时视频流识别出“我” 终于到了最后一步,激动时刻就要来临了,先平复一下心情,把剩下的代码加上,首先是为Model类增加一个预测函数: #识别人脸 ...
- IDEA 破解图文教程 - 2018.9 更
你好!这里是你要找的 IDEA 破解方法 目录 一.前言 二.IDEA 安装 2.1 下载IDEA 旗舰版 2.2 开始安装 2.3 自定 ...
- Github三次学习
作者声明:本文参照aicoder.com马伦老师的文档,根据自己的学习情况而做的笔记,仅供交流学习. Git入门到高级教程 为什么要进行项目文件的版本管理 代码备份和恢复 团队开发和协作流程 项目分支 ...
- java面向对象的有序数组和无序数组的比较
package aa; class Array{ //定义一个有序数组 private long[] a; //定义数组长度 private int nElems; //构造函数初始化 public ...