[Android测试] AS+Appium+Java+Win 自动化测试之八:使用PageObject模式和重封装
来源:互联网 发布:生物统计 知乎 编辑:程序博客网 时间:2024/06/09 23:25
一、 What? 什么是PageObject?
简称PO,这是一个设计模式,其实设计模式就是代码的架构,一个整体的框架。例如mvc 就是模型-视图-控制的一个代码架构,mvp就是-模型-视图-主持 这样的一个架构。PageObject翻译过来就是页面对象的意思,就是把页面对象和逻辑操作分开。结合封装,更加方便使用(不明白? 下面看demo)
二、 PageObject的好处
做UI自动化时定位特别依赖页面,一旦页面发生变更就不得不跟着去修改页面定位。
,假设你想对一个元素定位操作,你可能会编写下面的代码:
driver.findElement(By.id("comit")).click();
于是问题就出来了,这样代码冗余就高了。都写一块了,后期难维护。你有10个地方对这个元素做了这个操作,哪天这个控件的元素变了,你就不得不去修改10个地方。
那么po的好处就出来了,方便维护、减少代码冗余、逼格高
三、PO模式重封装
1.目录格式结构
- base //放封装好的基类
- cases //用例
- opeartion //放逻辑处理
- pages //页面元素处理
2. 重新封装
每层一个父类,driver使用静态,每个用例可以连续执行。封装类里面用到的Builder和Assertion在之前的文章已经贴出来了,这里就不再次贴了。
用例基类 InitAppium.java,每个用例继承这个类,直接加注解@Test,调用operate 层的方法即可
package com.example.base;import org.apache.http.util.TextUtils;import org.openqa.selenium.remote.DesiredCapabilities;import org.testng.annotations.AfterClass;import org.testng.annotations.AfterTest;import org.testng.annotations.BeforeSuite;import org.testng.annotations.Listeners;import java.io.File;import java.net.MalformedURLException;import java.net.URL;import io.appium.java_client.android.AndroidDriver;import io.appium.java_client.android.AndroidElement;/** * 测试用例的父类 * Created by LITP on 2016/9/7. */@Listeners({com.example.base.AssertionListener.class})public class InitAppium { //调试设备名字 public static String deviceName = "minote"; //调试设备系统版本 public static String platformVersion = "4.4.2"; //app路径 public static String appPath = System.getProperty("user.dir") + "/src/main/java/apps/shouhu2.2.3.apk"; //包名 public static String appPackage = "com.minstone.mdoctor"; //是否需要重新安装 public static String noReset = "True"; //是否不重新签名 public static String noSign = "True"; //是否使用unicode输入法,真是支持中文 public static String unicodeKeyboard = "True"; //是否祸福默认呢输入法 public static String resetKeyboard = "True"; //要启动的Activity //public static String appActivity = appPackage + ".activity.login.WelcomeActivity"; public static String appActivity = ""; public static AndroidDriver<AndroidElement> driver = null; //构造方法 public InitAppium() { this(new Builder()); } public InitAppium(Builder builder) { appActivity = builder.appActivity; appPackage = builder.appPackage; appPath = builder.appPath; deviceName = builder.deviceName; noReset = builder.noReset; noSign = builder.noSign; unicodeKeyboard = builder.unicodeKeyboard; resetKeyboard = builder.resetKeyboard; } /** * appium启动参数 * * @throws MalformedURLException */ @BeforeSuite public void beforeSuite() throws MalformedURLException { DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability("deviceName", deviceName); capabilities.setCapability("platformVersion", platformVersion); capabilities.setCapability("app", new File(appPath).getAbsolutePath()); capabilities.setCapability("appPackage", appPackage); //支持中文 capabilities.setCapability("unicodeKeyboard", unicodeKeyboard); //运行完毕之后,变回系统的输入法 capabilities.setCapability("resetKeyboard", resetKeyboard); //不重复安装 capabilities.setCapability("noReset", noReset); //不重新签名 capabilities.setCapability("noSign", noSign); //打开的activity if(!TextUtils.isEmpty(appActivity)){ capabilities.setCapability("appActivity", appActivity); } //启动Driver driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"), capabilities); } @AfterTest public void afterTest() { driver.quit(); } @AfterClass public void afterClass(){ //每一个用例完毕结束这次测试 //driver.quit(); } /** * 打印字符 * * @param str 要打印的字符 */ public <T> void print(T str) { if (!TextUtils.isEmpty(String.valueOf(str))) { System.out.println(str); } else { System.out.println("输出了空字符"); } }}
逻辑操作基类OperateAppium.java:,逻辑功能类继承这个类,在这里获取页面控件使用page层的对象
package com.example.base;import org.apache.http.util.TextUtils;import org.openqa.selenium.By;import org.openqa.selenium.TimeoutException;import java.util.List;import java.util.concurrent.TimeUnit;import io.appium.java_client.MultiTouchAction;import io.appium.java_client.TouchAction;import io.appium.java_client.android.AndroidDriver;import io.appium.java_client.android.AndroidElement;import static com.example.base.InitAppium.appPackage;import static io.appium.java_client.android.AndroidKeyCode.BACKSPACE;import static io.appium.java_client.android.AndroidKeyCode.KEYCODE_MOVE_END;/** * 逻辑处理父类 * Created by LITP on 2016/9/22. */public class OperateAppium { AndroidDriver driver; //单个触摸操作类 TouchAction touchAction; //多个触摸操作时间 MultiTouchAction multiTouchAction; private static int WAIT_TIME = 10; //默认的等待控件时间 private final int SWIPE_DEFAULT_PERCENT = 5; //默认滑动百分比 public final String SWIP_UP = "UP", SWIP_DOWN = "DOWN", SWIP_LEFT = "LEFT", SWIP_RIGHT = "RIGHT"; public OperateAppium(AndroidDriver androidDriver) { this.driver = androidDriver; } /** * 打印字符 * * @param str 要打印的字符 */ public <T> void print(T str) { if (!TextUtils.isEmpty(String.valueOf(str))) { System.out.println(str); } else { System.out.println("输出了空字符"); } } /** * Click点击空格键 * * @param ae 要点击的控件 * @return 返回是否点击 */ public boolean clickView(AndroidElement ae) { return clickView(ae, ""); } /** * Click点击控件 * * @param ae 控件 * @param str 控件的文字描述,供错误时候输出 * @return 返回是否存在控件 */ public boolean clickView(AndroidElement ae, String str) { if (ae != null) { ae.click(); return true; } else { print(str + "为空,点击错误"); } return false; } /** * 线程休眠秒数,单位秒 * * @param s 要休眠的秒数 */ public void sleep(long s) { try { Thread.sleep(s); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 获取当前的activity,返回文件名 * * @return */ public String getCurrActivity() { String str = driver.currentActivity(); return str.substring(str.lastIndexOf(".") + 1); } /** * 获取触摸实例 * * @return */ public TouchAction getTouch() { if (driver == null) { print("单点触摸时候driver为空"); return null; } else { if (touchAction == null) { return new TouchAction(driver); } else { return touchAction; } } } /** * 获取多点触摸实例 * * @return */ public MultiTouchAction getMultiTouch() { if (driver == null) { print("多点触摸时候driver为空"); return null; } else { if (multiTouchAction == null) { return new MultiTouchAction(driver); } else { return multiTouchAction; } } } /** * 往控件输入字符串 * * @param ae 要输入的控件 * @param str 要输入的字符串 */ public void input(AndroidElement ae, String str) { if (ae == null) { print("控件为空,输入内容失败:" + str); } else { ae.sendKeys(str); } } public void swipeToUp(int during) { swipeToUp(during, SWIPE_DEFAULT_PERCENT); } /** * 向上滑动, * * @param during */ public void swipeToUp(int during, int percent) { int width = getScreenWidth(); int height = getScreenHeight(); driver.swipe(width / 2, height * (percent - 1) / percent, width / 2, height / percent, during); } public void swipeToDown(int during) { swipeToDown(during, SWIPE_DEFAULT_PERCENT); } /** * 向下滑动, * * @param during 滑动时间 */ public void swipeToDown(int during, int percent) { int width = getScreenWidth(); int height = getScreenHeight(); driver.swipe(width / 2, height / percent, width / 2, height * (percent - 1) / percent, during); } public void swipeToLeft(int during) { swipeToLeft(during, SWIPE_DEFAULT_PERCENT); } /** * 向左滑动, * * @param during 滑动时间 * @param percent 位置的百分比,2-10, 例如3就是 从2/3滑到1/3 */ public void swipeToLeft(int during, int percent) { int width = getScreenWidth(); int height = getScreenHeight(); driver.swipe(width * (percent - 1) / percent, height / 2, width / percent, height / 2, during); } public void swipeToRight(int during) { swipeToRight(during, SWIPE_DEFAULT_PERCENT); } /** * 向右滑动, * * @param during 滑动时间 * @param percent 位置的百分比,2-10, 例如3就是 从1/3滑到2/3 */ public void swipeToRight(int during, int percent) { int width = getScreenWidth(); int height = getScreenHeight(); driver.swipe(width / percent, height / 2, width * (percent - 1) / percent, height / 2, during); } /** * 显示等待,等待Id对应的控件出现time秒,一出现马上返回,time秒不出现也返回 */ public AndroidElement waitAuto(By by, int time) { try { return new AndroidDriverWait(driver, time) .until(new ExpectedCondition<AndroidElement>() { @Override public AndroidElement apply(AndroidDriver androidDriver) { return (AndroidElement) androidDriver.findElement(by); } }); } catch (TimeoutException e) { print("查找元素超时!! " + time + " 秒之后还没找到元素 [" + by.toString() + "]"); return null; } } public AndroidElement waitAutoById(String id) { return waitAutoById(id, WAIT_TIME); } public AndroidElement waitAutoById(String id, int time) { return waitAuto(By.id(id), time); } public AndroidElement waitAutoByName(String name) { return waitAutoByName(name, WAIT_TIME); } public AndroidElement waitAutoByName(String name, int time) { return waitAuto(By.name(name), time); } public AndroidElement waitAutoByXp(String xPath) { return waitAutoByXp(xPath, WAIT_TIME); } public AndroidElement waitAutoByXp(String xPath, int time) { return waitAuto(By.xpath(xPath), time); } public void waitAuto() { waitAuto(WAIT_TIME); } /** * ,隐式等待,如果在指定时间内还是找不到下个元素则会报错停止脚本 * 全局设定的,find控件找不到就会按照这个事件来等待 * * @param time 要等待的时间 */ public void waitAuto(int time) { driver.manage().timeouts().implicitlyWait(time, TimeUnit.SECONDS); } /** * 打开Activity * * @param activityName activity的名字 */ public void startActivity(String activityName) { driver.startActivity(appPackage, activityName); } /** * 获取当前界面的所有EditText,并依次输入内容 * * @param str 要输入的数组 */ public void inputManyText(String... str) { List<AndroidElement> textFieldsList = driver.findElementsByClassName("android.widget.EditText"); for (int i = 0; i < str.length; i++) { textFieldsList.get(i).click(); clearText(textFieldsList.get(i)); //清除内容 textFieldsList.get(i).sendKeys(str[i]); } } /** * 点击屏幕中间 */ public void press() { press(getScreenWidth() / 2, getScreenHeight() / 2); } /** * 点击某个控件 * * @param ae 要点击的控件 */ public void press(AndroidElement ae) { try { getTouch().tap(ae).perform(); } catch (Exception e) { print("tab点击元素错误" + e.getMessage()); e.printStackTrace(); } } /** * 点击某个坐标 * * @param x * @param y */ public void press(int x, int y) { try { driver.tap(1, x, y, 500); //getTouch().tap(x, y).perform(); print("tab点击位置(" + x + "," + y + ")"); } catch (Exception e) { print("tab点击元素位置异常" + e.getMessage()); e.printStackTrace(); } } /** * 长按某个控件 * * @param ae 要点击的控件 */ public void longPress(AndroidElement ae) { try { getTouch().longPress(ae).release().perform(); } catch (Exception e) { print("长按点击元素错误" + e.getMessage()); e.printStackTrace(); } } /** * 长按某个坐标 * * @param x * @param y */ public void longPress(int x, int y) { try { getTouch().longPress(x, y).release().perform(); } catch (Exception e) { print("长按点击元素错误" + e.getMessage()); e.printStackTrace(); } } /** * 在控件上滑动 * * @param element 要滑动的控件 * @param direction 方向,事件不设置默认1秒 */ public void swipOnElement(AndroidElement element, String direction) { swipOnElement(element, direction, 1000); //不设置时间就为2秒 } /** * 在某一个控件上滑动 * * @param element 在那个元素上滑动 * @param direction 方向,UP DOWN LEFT RIGHT */ public void swipOnElement(AndroidElement element, String direction, int duration) { //获取元素的起初xy,在左上角 int x = element.getLocation().getX(); int y = element.getLocation().getY(); //获取元素的宽高 int width = element.getSize().getWidth(); int height = element.getSize().getHeight(); switch (direction) { case SWIP_UP: int startX = x + width / 2; //在4/5的底部的中间向上滑动 driver.swipe(startX, y + height * 4 / 5, startX, y + height / 5, duration); break; case SWIP_DOWN: startX = x + width / 2; //在4/5的底部的中间向上滑动 driver.swipe(startX, y + height / 5, startX, y + height * 4 / 5, duration); break; case SWIP_LEFT: int startY = y + width / 2; driver.swipe(x + width * 4 / 5, startY, x + width / 5, startY, duration); break; case SWIP_RIGHT: startY = y + width / 2; driver.swipe(x + width / 5, startY, x + width * 4 / 5, startY, duration); break; } } /** * 在某个方向上滑动 * * @param direction 方向,UP DOWN LEFT RIGHT * @param duration 持续时间 */ public void swip(String direction, int duration) { switch (direction) { case "UP": swipeToUp(duration); break; case "DOWN": swipeToDown(duration); break; case "LEFT": swipeToLeft(duration); break; case "RIGHT": swipeToRight(duration); break; } } /** * 在指定次数的条件下,某个方向滑动,直到这个元素出现 * * @param by 控件 * @param direction 方向,UP DOWN LEFT RIGHT * @param duration 滑动一次持续时间 * @param maxSwipNum 最大滑动次数 */ public void swipUtilElementAppear(By by, String direction, int duration, int maxSwipNum) { int i = maxSwipNum; Boolean flag = true; while (flag) { try { if (i <= 0) { flag = false; } driver.findElement(by); flag = false; } catch (Exception e) { i--; swip(direction, duration); } } } /** * 在某个方向滑动直到这个元素出现 * * @param by 控件 * @param direction 方向,UP DOWN LEFT RIGHT * @param duration 滑动一次持续时间 */ public void swipUtilElementAppear(By by, String direction, int duration) { Boolean flag = true; while (flag) { try { driver.findElement(by); flag = false; } catch (Exception e) { swip(direction, duration); } } } /** * 获取屏幕的宽高 * * @return 返回宽高的数组 */ public int[] getScreen() { int width = driver.manage().window().getSize().getWidth(); int height = driver.manage().window().getSize().getHeight(); return new int[]{width, height}; } /** * 获取屏幕宽度 * * @return */ public int getScreenWidth() { return driver.manage().window().getSize().getWidth(); } /** * 获取屏幕高度 * * @return */ public int getScreenHeight() { return driver.manage().window().getSize().getHeight(); } /** * 逐字删除编辑框中的文字 * * @param element 文本框架控件 */ public void clearText(AndroidElement element) { String text = element.getText(); //跳到最后 driver.pressKeyCode(KEYCODE_MOVE_END); for (int i = 0; i < text.length(); i++) { //循环后退删除 driver.pressKeyCode(BACKSPACE); } }}
页面元素基类PageAppium.java:, 界面元素的存放获取类继承这个类,
package com.example.base;import org.apache.http.util.TextUtils;import org.openqa.selenium.By;import org.openqa.selenium.NoSuchElementException;import org.openqa.selenium.TimeoutException;import java.util.ArrayList;import java.util.List;import java.util.concurrent.TimeUnit;import io.appium.java_client.android.AndroidDriver;import io.appium.java_client.android.AndroidElement;import static com.example.base.InitAppium.appPackage;/** * 页面UI获取定位父类,供给Page层使用 * Created by LITP on 2016/9/23. */public class PageAppium { AndroidDriver driver; private static int WAIT_TIME = 3; //默认的等待控件时间 public PageAppium(AndroidDriver androidDriver) { this.driver = androidDriver; waitAuto(WAIT_TIME); } public boolean isIdElementExist(String id) { return isIdElementExist(id, 0); } public boolean isIdElementExist(String id,int timeOut) { return isIdElementExist(id,timeOut ,false); } /** * 根据id判断当前界面是否存在并显示这个控件 * * @param id 要查找的id * @param isShow 是否判断控件显示 * @return 返回对应的控件 */ public boolean isIdElementExist(String id,int timeOut, boolean isShow) { return isElementExist(By.id(appPackage + ":id/" +id),timeOut,isShow); } /** * 选择当前界面的有这个文字的控件 * * @param name * @return */ public boolean isNameElementExist(String name) { return isNameElementExist(name, 0); } public boolean isNameElementExist(String name, int timeOut) { return isNameElementExist(name, timeOut,false); } public boolean isNameElementExist(String name, int timeOut, boolean isShow) { return isElementExist(By.name(name),timeOut, isShow); } /** * 判断当前界面有没有这个字符串存在 * * @param text 要判断的字符串 * @return 存在返回真 */ public boolean isTextExist(String text) { String str = driver.getPageSource(); print(str); return str.contains(text); } /** * 判断当前界面有没有这个Xpath控件存在 * * @param text 要判断的字符串 * @return 存在返回真 */ public boolean isXpathExist(String text) { return isXpathExist(text,0); } public boolean isXpathExist(String text,int timeOut) { return isXpathExist(text,timeOut, false); } public boolean isXpathExist(String text,int timeOut,boolean isShow) { ////android.widget.TextView[@text='"+text+"'] return isElementExist(By.xpath(text), timeOut,isShow); } /** * 判断控件时候存在 * * @param by By * @param timeout 等待的事件 * @return */ public boolean isElementExist(By by, int timeout,boolean isShow) { try { AndroidElement element = waitAuto(by, timeout); if(element == null){ return false; }else{ if(isShow){ return element.isDisplayed(); } } return true; } catch (Exception e) { return false; } } /** * 获取当前的activity,返回文件名 * * @return */ public String getCurrActivity() { String str = driver.currentActivity(); return str.substring(str.lastIndexOf(".") + 1); } /** * 根据id获取当前界面的一个控件 * * @param id 要查找的id * @return 返回对应的控件 */ public AndroidElement findById(String id,String desc) { return findElementBy(By.id(id),desc); } public AndroidElement findById(String id) { return findElementBy(By.id(id),""); } public AndroidElement findByFullId(String id) { try { if (driver != null) { return (AndroidElement) driver.findElement(By.id(id)); } else { print("driver为空"); } } catch (NoSuchElementException e) { print("找不到控件:" +" 异常信息:"+ e.getMessage()); } return null; } /** * 选择当前界面的有这个文字的控件 * * @param name 内容 * @return 找到的控件 */ public AndroidElement findByName(String name,String desc) { return findElementBy(By.name(name),desc); } public AndroidElement findByName(String name) { return findByName(name,""); } /** * 根据id获取当前界面的一个控件 * * @param name 要查找的控件的类名 * @return 返回对应的控件 */ public AndroidElement findByClassName(String name,String desc) { return findElementBy(By.className(name),desc); } public AndroidElement findByClassName(String name) { return findByClassName(name,""); } public AndroidElement findByXpath(String str) { return findByXpath(str,""); } public AndroidElement findByXpath(String str,String desc) { return findElementBy(By.xpath(str),desc); } public AndroidElement findElementBy(By by){ return findElementBy(by,""); } /** * 获取控件 * @param by by * @param str 报错提示信息 * @return */ public AndroidElement findElementBy(By by,String str){ try { if (driver != null) { return (AndroidElement) driver.findElement(by); } else { print("driver为空"); } } catch (NoSuchElementException e) { print("找不到控件:" +str+" 异常信息:"+ e.getMessage()); } return null; } /** * 打印字符 * * @param str 要打印的字符 */ public <T> void print(T str) { if (!TextUtils.isEmpty(String.valueOf(str))) { System.out.println(str); } else { System.out.println("输出了空字符"); } } /** * 线程休眠秒数,单位秒 * * @param s 要休眠的秒数 */ public void sleep(long s) throws InterruptedException { Thread.sleep(s); } /** * 显示等待,等待Id对应的控件出现time秒,一出现马上返回,time秒不出现也返回 */ public AndroidElement waitAuto(By by, int time) { try { return new AndroidDriverWait(driver, time) .until(new ExpectedCondition<AndroidElement>() { @Override public AndroidElement apply(AndroidDriver androidDriver) { return (AndroidElement) androidDriver.findElement(by); } }); } catch (TimeoutException e) { return null; } } public AndroidElement waitAutoById(String id) { return waitAutoById(id, WAIT_TIME); } public AndroidElement waitAutoById(String id, int time) { return waitAuto(By.id(id), time); } public AndroidElement waitAutoByName(String name) { return waitAutoByName(name, WAIT_TIME); } public AndroidElement waitAutoByName(String name, int time) { return waitAuto(By.name(name), time); } public AndroidElement waitAutoByXp(String xPath) { return waitAutoByXp(xPath, WAIT_TIME); } public AndroidElement waitAutoByXp(String xPath, int time) { return waitAuto(By.xpath(xPath), time); } public void waitAuto() { waitAuto(WAIT_TIME); } /** * ,隐式等待,如果在指定时间内还是找不到下个元素则会报错停止脚本 * 全局设定的,find控件找不到就会按照这个事件来等待 * * @param time 要等待的时间 */ public void waitAuto(int time) { driver.manage().timeouts().implicitlyWait(time, TimeUnit.SECONDS); } /** * 获取屏幕的宽高 * * @return 返回宽高的数组 */ public int[] getScreen() { int width = driver.manage().window().getSize().getWidth(); int height = driver.manage().window().getSize().getHeight(); return new int[]{width, height}; } /** * 获取屏幕宽度 * * @return */ public int getScreenWidth() { return driver.manage().window().getSize().getWidth(); } /** * 获取屏幕高度 * * @return */ public int getScreenHeight() { return driver.manage().window().getSize().getHeight(); } /** * 根据ClassName获取多个控件 * * @param className 控件的类名字,例如 android.widget.EditText * @param num 返回的数量 * @return */ public List<AndroidElement> getManyElementByClassName(String className, int num) { List<AndroidElement> textFieldsList = driver.findElementsByClassName(className); List<AndroidElement> list = new ArrayList<>(); try{ for(int i=0; i<num; i++){ list.add(textFieldsList.get(i)); } return list; }catch (Exception e){ print("获取多个控件异常"+e.getMessage()); } return null; } /** * 根据Id获取多个控件 * * @param id 控件的类名字,例如 android.widget.EditText * @param num 返回的数量 * @return */ public List<AndroidElement> getManyElementById(String id, int num) { if(driver != null){ List<AndroidElement> textFieldsList = driver.findElementsById(id); List<AndroidElement> list = new ArrayList<>(); try{ for(int i=0; i<num; i++){ list.add(textFieldsList.get(i)); } return list; }catch (Exception e){ print("获取多个控件异常"+e.getMessage()); } }else{ print("获取多个控件"+id+"时候driver为空"); } return null; } /** * 获取同id的list的控件 * @param id id * @param num 取那一个控件 * @return */ public AndroidElement getListOneElementById(String id,int num){ if(driver != null){ try{ return (AndroidElement) driver.findElementsById(appPackage+":id/"+id).get(num); }catch (Exception e){ print("getListOneElementById找不到第"+num+"个控件"+id); return null; } }else{ print("getListOneElementById:"+id+" 时候driver为空"); return null; } }}
下一篇讲解案例,这篇的案例和封装代码都在下一篇博客
2 0
- [Android测试] AS+Appium+Java+Win 自动化测试之八:使用PageObject模式和重封装
- AS+Appium+Java+Win 自动化测试之八:使用PageObject模式和重封装
- [Android测试] AS+Appium+Java+Win 自动化测试之六 Appium的Java测试脚本封装
- [Android测试] AS+Appium+Java+Win 自动化测试之六 Appium的Java测试脚本封装
- [Android测试] AS+Appium+Java+Win自动化测试之三: 基础知识和Appium界面
- [Android测试] AS+Appium+Java+Win 自动化测试之四: 单元测试框架和TestNg
- [Android测试] AS+Appium+Java+Win 自动化测试之十:testng多设备并行测试实例封装
- [Android测试] AS+Appium+Java+Win 自动化测试之七: 写脚本测试自己的app
- [Android测试] AS+Appium+Java+Win 自动化测试之九:PO模式的实例与ReportNg测试报告
- [Android测试] AS+Appium+Java+Win 自动化测试之五:脚本重点技术
- Android自动化测试之appium的使用
- 关于app自动化测试pageObject设计模式
- Android Studio下使用JAVA+Appium进行自动化测试及简单封装
- [Android测试] Android Studio+Appium+Java+Windows 自动化测试之二:Appium环境安装搭建
- Android Appium自动化测试
- Appium自动化测试Android
- APPIUM+JAVA自动化测试
- appium 自动化测试之Android客户端
- Codeforces723 C. Polycarp at the Radio (模拟)
- redis--事务处理与乐观锁
- [LeetCode-Java]31. Next Permutation
- 栈结构的四则运算
- 数据搜索的相关资料
- [Android测试] AS+Appium+Java+Win 自动化测试之八:使用PageObject模式和重封装
- P,V操作
- hashcode和equals方法
- JAVA-泛型
- Just a test.
- C语言string.h常用函数总结
- Ext中如何定义一个类(define)
- Expedition(POJ 2431) 优先队列
- |洛谷|NOIP2007|动态规划|P1095 守望者的逃离