赞
踩
http://110.40.155.17/download/
把SDK下的这两个目录位置加入PATH环境变量即可
D:\SDK\androidsdk\platform-tools
D:\SDK\androidsdk\tools
为了防止可能存在的SDK冲突,建议将夜神模拟器安装目录下的nx-adb.exe替换为我们安装的SDK目录的adb.exe
ADB全名Android Debug Bridge,是一个调试工具
工作原理
abd工具可以在电脑通过终端命令来操作安卓手机/模拟器
adb connect 127.0.0,1:62001
自动化测试需要通过代码的形式告诉手机测试那个应用程序的哪一个界面,所以需要通过某种方式定位到某个应用程序的某个页面。
获取包名和界面名的命令:
adb shell dumpsys window windows | grep mFocusedApp
adb shell dumpsys window windows | findstr mFocusedApp
获取当前手机上打开正在显示的那个应用程序的包名和界面名
mFocusedApp=AppWindowToken{43fda26 token=Token{51a168 ActivityRecord{ffecc8b u0 com.android.settings/.Settings t3}}}
adb push 电脑文件路径 手机文件夹路径
C:\Users\zdh\Desktop\stu.sql
adb pull 手机的文件路径 电脑的文件夹路径
例如:
adb pull /sdcard/stu.sql C:\Users\hhh
adb shell am start -W 包名/启动名(界面名)
例如: 我们点击设置程序,看看它的启动时间(我们只需要执行下面的命令,该命令会发送给对应的手机和模拟器,然后对应会去打开设置应用程序,记录相关时间参数后,返回给我们)
adb shell am start -W com.android.settings/.Settings
参数解释:
adb logcat
安装app到手机
adb install 路径/xx,apk
卸载手机上的app,需要指定包名--获取应用程序包名的方法上面给出了
adb uninstall 包名
adb shell
adb start-server
adb kill-server
adb --help
save as Preset是保存当前预设配置
填写完毕后,先保存配置,然后点击start session开启会话即可
appium提供的元素探测
原生的探测工具
查询当前聚焦的页面类名
adb shell dumpsys window windows | findstr mFocusedApp
需要先进入sdk安装目录的build-tools目录下
aapt dump badging 应用apk的路径 | findstr package
aapt dump badging 应用apk的路径 | findstr launchable-activity
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>7.6.0</version>
</dependency>
@Test public void test() throws MalformedURLException { //1.创建配置对象 DesiredCapabilities desiredCapabilities=new DesiredCapabilities(); //2.添加配置 //deviceName:找到测试的设备 desiredCapabilities.setCapability("deviceName","127.0.0.1:62001"); //platformName:测试平台 desiredCapabilities.setCapability("platformName","Android"); //appPackage:测试app包名 desiredCapabilities.setCapability("appPackage","com.android.browser"); //appActivity:测试App启动入口 desiredCapabilities.setCapability("appActivity",".BrowserActivity"); //3.创建驱动 //appium的通讯地址和配置对象 AndroidDriver<WebElement> androidDriver=new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"),desiredCapabilities); }
adb shell dumpsys window windows | findStr mFocusedApp: 命令可以获取到我们要测试app的包名和对应的界面名
这里通过测试登录qq为案例
public class TestOne { @Test public void test() throws MalformedURLException, InterruptedException { //1.准备,并打开qq程序 AndroidDriver<WebElement> androidDriver = prepare("com.tencent.mobileqq", ".activity.LoginActivity"); //2.定位然后操作元素 //点击同意按钮 androidDriver.findElementById("com.tencent.mobileqq:id/dialogRightBtn").click(); //等待程序更新--休眠10s Thread.sleep(15000); System.out.println("点击登录按钮"); //点击登录按钮 TouchAction touchAction=new TouchAction(androidDriver); touchAction.tap(PointOption.point(500,1470)).release().perform(); //等待页面刷新 Thread.sleep(3000); System.out.println("输入账号和密码"); //输入qq账号 androidDriver.findElementByAccessibilityId("请输入QQ号码或手机或邮箱").sendKeys("xxxx"); //输入qq密码 androidDriver.findElementById("com.tencent.mobileqq:id/password").sendKeys("xxx"); //点击登录 androidDriver.findElementByAccessibilityId("登 录").click(); Thread.sleep(10000); //退出 androidDriver.quit(); } public AndroidDriver<WebElement> prepare(String appPackage,String appActivity) throws MalformedURLException { //1.创建配置对象 DesiredCapabilities desiredCapabilities=new DesiredCapabilities(); //2.添加配置 //deviceName:找到测试的设备 desiredCapabilities.setCapability("deviceName","127.0.0.1:62001"); //platformName:测试平台 desiredCapabilities.setCapability("platformName","Android"); //appPackage:测试app包名 desiredCapabilities.setCapability("appPackage",appPackage); //appActivity:测试App启动入口 desiredCapabilities.setCapability("appActivity",appActivity); //更换新引擎--uiautomator2解决输入框输入不了数据 desiredCapabilities.setCapability("automationName","uiautomator2"); //3.创建驱动 //appium的通讯地址和配置对象 return new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"),desiredCapabilities); } }
对于安卓应用来说,Appium会往对应安卓手机上推送一个Bootstrap.jar并运行它,当我们自动化测试程序向appium发送请求时,appium向Boostrap.jar发送请求,由Bootstrap.jar转发请求到安卓手机底层的自动化测试框架UIAutomator。
再由底至上,将测试结果最终返回给我们的测试程序。
通过appium初始化日志分析得到
//如果resource-id唯一,那么使用下面这个方法就行
//如果存在多个元素resource-id相同,那么下面api默认选择第一个
androidDriver.findElementById();
//如果存在多个元素resource-id相同,使用下面api---返回一个list
androidDriver.findElementsById();
androidDriver.findElement(By.name(""));
androidDriver.findElementByAndroidUIAutomator("new UiSelector().text('登录')");
根据class属性去找元素,一般在页面中很多元素的class属性都是一致的,所以这种方式基本不用。
//使用相对定位---//开头
androidDriver.findElementByXPath("//android.widget.Button[@text='登录']").click();
在UIAutomatorViewer并没有此属性,对应是content-desc属性
androidDriver.findElementByAccessibilityId("登 录").click();
//不清除应用数据启动测试
desiredCapabilities.setCapability("noReset","true");
设置固定等待时间,即便不需要等待即可定位到元素,依然要求进行等待
Thread.sleep();
针对全局元素设置等待时间,服务端(Appium)会在特定的超时时间内重试多次寻找控件
androidDriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
隐式等待就是在设置的时间范围内整个页面元素加载出来,然后再轮询页面元素直到寻找成功,如果超出时间后仍然未找到元素则继续往下面执行。
同时要注意的是,driver设置的隐式等待时间会对当前driver的整个生命周期都生效,直到调用driver.close()方法。因此,通过driver定位每一个元素都会有隐式等待的时间,这会影响测试脚本执行的效率
针对某个元素设置等待时间,服务端(Appium)会在特定的超时时间内重试多次寻找控件
WebDriverWait wait = (WebDriverWait) new WebDriverWait(androidDriver,
Duration.ofSeconds(10).getSeconds(),Duration.ofSeconds(1).getSeconds())
.ignoring(NoSuchElementException.class)
.ignoring(WebDriverException.class)
.ignoring(UnreachableBrowserException.class)
.ignoring(ProtocolException.class);
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("continueid")));
element.click();
/**
* @param startPointX 滑动起始坐标x
* @param startPointY 滑动起始坐标y
* @param endPointX 滑动结束坐标x
* @param endPointY 滑动结束坐标y
* @param duration 滑动耗时--默认毫秒
*/
public void swipe(AndroidDriver<WebElement> androidDriver, int startPointX, int startPointY, int endPointX, int endPointY, int duration){
TouchAction touchAction = new TouchAction(androidDriver);
touchAction.press(PointOption.point(startPointX,startPointY)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(duration)))
.moveTo(PointOption.point(endPointX,endPointY)).release();
//让滑动生效
touchAction.perform();
}
具体代码如下:
public void test(AndroidDriver<WebElement> androidDriver){
TouchAction touchAction = new TouchAction(androidDriver);
PointOption point1 = PointOption.point(150, 427);
PointOption point2 = PointOption.point(362, 427);
PointOption point3 = PointOption.point(569, 427);
PointOption point4 = PointOption.point(359, 625);
PointOption point5 = PointOption.point(150, 850);
PointOption point6 = PointOption.point(362, 850);
PointOption point7 = PointOption.point(569, 850);
touchAction.press(point1).moveTo(point2).moveTo(point3).moveTo(point4).moveTo(point5).moveTo(point6).moveTo(point7)
.release();
touchAction.perform();
}
public void test(AndroidDriver<WebElement> androidDriver){ MultiTouchAction multiTouchAction=new MultiTouchAction(androidDriver); //得到当前屏幕的高度和宽度 int x = androidDriver.manage().window().getSize().getWidth(); int y = androidDriver.manage().window().getSize().getHeight(); //第一根手指从B点滑动到A点 TouchAction touchAction1 = new TouchAction<>(androidDriver); touchAction1.press(PointOption.point(x*4/10,y*4/10)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000))) .moveTo(PointOption.point(x*2/10,y*2/10)).release(); //第二根手指从C点滑动到D点 TouchAction touchAction2 = new TouchAction<>(androidDriver); touchAction2.press(PointOption.point(x*6/10,y*6/10)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000))) .moveTo(PointOption.point(x*8/10,y*8/10)).release(); //将两根手指的动作添加到MultiTouchAction里面 multiTouchAction.add(touchAction1).add(touchAction2); multiTouchAction.perform(); }
实现页面跳转,包括APP内部页面和APP相互跳转
adb shell dumpsys window windows | findstr mFocusedApp
/**
* 当前APP内部页面跳转
*/
public void moveToPage(String appActivity){
androidDriver.startActivity(new Activity("com.tencent.mobileqq", appActivity));
}
/**
* 实现APP相互跳转---只能跳转到另一个app的启动页面
*/
public void moveToPage(String AppPackage,String appActivity){
androidDriver.startActivity(new Activity(AppPackage, appActivity));
}
String pageSource = androidDriver.getPageSource();
System.out.println(pageSource);
可以用于断言当前页面是否已经有了某个元素,或者判断当前页面有没有产生变化,如上下滚动判断是否已经已经到了底端或者顶端。
<?xml version="1.0" encoding="UTF-8"?>
<hierarchy rotation="0">
<android.widget.FrameLayout index="0" text="" ....
String currentActivity = androidDriver.currentActivity();
androidDriver.resetApp();
有些场景我们需要清除应用的数据,相当于第一次安装时候的状态,比如: 第一次启动app的引导页,登录等等。
boolean appInstalled = androidDriver.isAppInstalled("");
Android平台独有,向系统发送键值事件,不同的键值对应不同的功能,如: keyevent(4)表示手机的HOME按键
public void pressKey(AndroidKey androidKey){
//1.创建KeyEvent对象
KeyEvent keyEvent = new KeyEvent();
//2.使用withKey传入键值
keyEvent.withKey(androidKey);
//3.使用pressKey发送键值
androidDriver.pressKey(keyEvent);
}
当测试用例执行失败之后进行屏幕截图,保存到本地为了更好的查找问题。
File imgFile = androidDriver.getScreenshotAs(OutputType.FILE);
//获取设备时间
System.out.println("当前时间为:"+androidDriver.getDeviceTime());
//获取设备DPI
System.out.println("当前屏幕DPI:"+androidDriver.getDisplayDensity());
//获取当前自动化引擎
System.out.println("当前引擎为:"+androidDriver.getAutomationName());
//获取设备横竖屏状态
System.out.println("当前横竖屏状态:"+androidDriver.getOrientation());
上面都是对原生Native app的测试,下面开始讲解,如何完成对Hybrid APP(混合型—>Native+H5)进行自动化测试
手机端页面分为两类,一类使用原生安卓开发,另一类使用原生安卓加h5页面混合而成,对于原生页面的自动化测试,就如上面所讲,而要把上面对原生安卓页面的自动化测试放到webView–>web页面上来的时候,则无法生效,需要进行特殊处理。
Appium提供对Hybrid app进行自动化测试的方法= 基于UIAutomator+ChromeDriver
准备工作:
如何区分原生界面和web界面呢?
如果是第三方线上app,一般webview debug开关都是关闭的,这就需要借助第三方工具,才能将debug开关打开。
解决方法
Xposed是一个框架,能够集成很多功能模块,这些模块能够在不修改APK的情况下,修改APP的运行方式,这里我们需要WebViewDebugHook模块来开启APP的WebView debug模式。
手机或者模拟器需要开启ROOT模式
http://110.40.155.17/download/
我们需要一个Hybrid的线上app进行测试,这里选择58同城
每一种页面都存在一种上下文,要定位到web页面里面的元素,需要切换到对应的context中,然后进行元素定位。
//1.进入web页面中---text文本值定位到新车元素
androidDriver.findElementByAndroidUIAutomator("new UiSelector().text('新车')").click();
//2.获取到所有的contexts
Set<String> contextHandles = androidDriver.getContextHandles();
System.out.println(contextHandles);
//3.切换到webview对应的上下文
androidDriver.context("WEBVIEW_com.wuba");
Thread.sleep(2000);
//4.元素定位,然后进行相关操作
androidDriver.findElementByXPath("").click();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。