【前言】
编写安卓自动化测试代码,本文选择的是夜神模拟器+Appium
【语言选择】
Appium使用的是C/S架构方式,Client端可以支持的编程语言挺多的,本文选择的是Java
【IDE选择】
编写Java代码,本文选择的IDE是eclipse
【eclipse基础配置】
使用Maven管理项目的依赖包
【依赖包】
在网站(https://mvnrepository.com/)中搜索需要安装的依赖包的Maven代码放入pom.xml文件中
【需要的依赖包】
1 java-client
2 testNG
启动夜神模拟器中的驾考宝典app的配置:
// 1 创建配置对象 DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); // 2 添加配置-测试的设备 desiredCapabilities.setCapability("deviceName", "127.0.0.1:62001"); // 2 添加配置-测试的平台 desiredCapabilities.setCapability("platformName", "Android"); // 2 添加配置-测试的App desiredCapabilities.setCapability("appPackage", "com.handsgo.jiakao.android"); // 2 添加配置-测试App的启动入口 desiredCapabilities.setCapability("appActivity", "com.handsgo.jiakao.android.splash.Login"); // 3 创建驱动(参数:Appium通讯地址 配置参数) androidDriver = new AndroidDriver<WebElement>(new URL("http://127.0.0.1:4723/wd/hub"), desiredCapabilities);
更改配置,解决输入框不能输入数据的问题
// 2 添加配置-更改自动化引擎来解决输入框输入不了数据的问题 desiredCapabilities.setCapability("automationName", "uiautomator2");
更改配置,不清除应用的数据启动测试(默认和设置为true时为不清除)
desiredCapbilities.setCapability("noReset", "true");
元素等待
在自动化过程中,元素的出现受到网络环境,设备性能等多种因素影响。因此元素加载的时间可能不一致,从而会导致元素无法定位超时报错。
因此设置元素等待可以更加灵活的制定等待定位元素的时间,从而增强脚本的健壮性,提高执行效率。
元素等待方式1:强制等待
固定的等待时间
Thread.sleep(8000);//等待8秒
元素等待方式2:隐式等待
针对全局元素设置等待时间,如果没有定位到UI元素,脚本可以在全局等待时间长度内进行反复查询定位,定位成功后会提前结束等待,继续接下来的执行任务
androidDriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
元素等待方式3:显示等待
针对某个元素设置等待时间
WebDriverWait webDriverWait = new WebDriverWait(androidDriver, 10); WebElement webElement = webDriverWait.until(new ExpectedCondition<WebElement>() { @Override public WebElement apply(WebDriver arg0) { return androidDriver.findElementById("com.handsgo.jiakao.android:id/btn_agree"); } }); webElement.click();
定位UI元素的方式
定位方式1:ID定位
ID就是一个控件的唯一身份标识,由开发人员在项目中指定,如果有对应的resource-id,可以采取这种方式来实现定位操作。
注意:有可能app开发人员并不是很严谨,一个界面会出现多个相同的id,这种情况下默认定位到第一个有此id的元素。
测试用例代码示例:
androidDriver.findElementById("com.handsgo.jiakao.android:id/btn_agree").click();
如果有多个相同id的情况:
List<WebElement> listElements = androidDriver.findElementById("com.handsgo.jiakao.android:id/btn_agree");
listElements.get(2).click();
定位方式2:text定位
Appium 1.5之前的版本支持By.name方式,比如:
// 根据text属性进行定位 androidDriver.findElementByname("上海");
Appium 1.5之后的版本不再支持By.name方式,需要使用UIAutonator原生自动化引擎,比如:
// 使用UIAutomator定位text属性元素 androidDriver.findElementByAndroidUIAutomator("new UiSelector().text("上海")");
【注意】如果该方式调用不出,检查一下jre的版本是否在1.5以上,更改方式如下:
第一步:
第二步:
第三步:
定位方式3:className定位(弃用)
根据class属性定位元素,一般在页面中很多元素的class属性一致,所以这种方式基本上不使用
定位方式4:xpath定位
Appium也可以支持xpath定位,在项目中此种方式能够使用多场景,xpath定位在web自动化测试中可以使用在app自动化测试中也可以使用,xpath定位有两种定位方式,一种是绝对位置定位另一种是相对位置定位,相对位置定位使用更加普遍,因为如果使用绝对位置定位UI元素有所改变的情况下,相关UI元素的xpath都需要相应进行改变。xpath相对路径需要使用到class属性和text属性内容进行拼接:
androidDriver.findElementByXPath("//android.widget.TextView[@text='上海']").click();
定位方式5:accessibility id定位
在UIAutomatorViewer并没有此属性,对应是content-desc属性 。
注意:很多UI元素没有这个属性
androidDriver.findElementByAccessibilityId("上海").click();
定位方式6:坐标定位
选择设置中关于手机-连续点击五次版本号-进入开发者选项-勾选指针位置
注意:坐标定位受设备屏幕尺寸/分辨率/DPI影响,万不得已不要使用此种方式定位元素
定位方式7:Toast元素的获取
获取要求:Java-client 5.0+、使用UIAutomator2自动化引擎、Android系统版本5.0+
获取方法:
WebElement toastElement = androidDriver.findElementByXPath("//*[contains(@text, '用户名与密码')]");
System.out.print(toastElement.getText());
对UI元素的操作
UI元素操作1:点击操作
.click();//点击
UI元素操作2:输入操作
.sendKeys("深圳");//输入
UI元素操作3:滑动手势
Java-clilent 5.0之前版本提供的滑动API
// Java-client 4.1.2 androidDriver.swipe(356, 594, 356, 794, 800); // (起始点坐标x, 起始点坐标y, 终止点坐标x, 终止点坐标y, 滑动时间)
Java-client 5.0之后需要自定义实现(可采用之前版本的实现思路)
// Java-client 6.1.0 TouchAction touchAction = new TouchAction(androidDriver); PointOption startPointOption = PointOption.point(356, 594); PointOption endPointOption = PointOption.point(356, 794); Duration duration = Duration.ofMillis(800); WaitOptions waitOptions = WaitOptions.waitOptions(duration); touchAction.press(startPointOption).waitAction(waitOptions).moveTo(endPointOption).release(); touchAction.perform();// 让滑动生效
滑动的使用场景:下拉刷新、手势解锁等
手势解锁需要滑动到多个点,代码上使用多个.moveTo()方法就行了:
touchAction.press(pointOption1).waitAction(waitOptions).moveTo(pointOption2).moveTo(pointOption3).moveTo(pointOption4).release();
UI元素操作4:多点触摸手势
使用场景:地图缩放
MultiTouchAction类可以模拟用户多点触摸操作
主要包含有add()和perform()两个方法
可以结合TouchAction模拟多根手指滑动效果
// 1 实例化MultiTouchAction对象 MultiTouchAction multiTouchAction = new MultiTouchAction(androidDriver); // 2 实例化两个TouchAction(因为需要两根手指进行放大操作) TouchAction touchAction1 = new TouchAction<>(androidDriver); TouchAction touchAction2 = new TouchAction<>(androidDriver); // 3 得到当前屏幕的高度和宽度 int x = androidDriver.manage().window().getSize().getWidth(); int y = androidDriver.manage().window().getSize().getHeight(); // 第一根手指的滑动 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(); // 第二根手指的滑动 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();
断言
使用testNG集成断言框架 ,选择src/test/java中的.java文件,右击选择“testNG”-->“Convert to TestNG”,如下图所示
(如果右击没有TestNG选项,可以在eclipse中的MarketPlace中搜索TestNG在线安装)
之后在项目中会自动生成一个testng.xml文件,在刚刚选中的java文件中会在一些方法前面有@Test标识,测试开始之前的初始化@BeforeTest标识,刻可以将之前放在main函数中的执行代码放置到beforeTest标识下的自定义setup方法中,然后把main函数去掉。使用@AfterTest标识的函数用来当测试用例运行完毕后销毁测试驱动(调用测试驱动对象的quit方法)。此后运行方式使用"Run As"--"TestNG Test"。
断言1:检查当前展示的界面是否是测试用例预期的结果
先确定预期界面类的名字,终端执行
adb shell dumpsys activity | find "mFocusedActivity"
输出的结果中,斜杠后面的一长串就是类名,例如下图所示
在测试用例中:
String expected = "cn.mucang.android.mars.student.refactor.business.inquiry.activity.InquiryActivuty"; String actual = androidDriver.currentActivity(); Assert.assertEquals(actual, expected);
运行完之后会有一个测试报告自动生成。
Appium常用API
Appium常用API1:实现页面跳转(startActivity)
页面跳转,包括App内部页面及App相互跳转
// 开启某一个activity,实现跳转 // 首先我们需要创建activity对象,用activity构建方法初始化,参数为对应的包名和类名 Activity activity = new Activity("com.lemon.lemonban", "com.lemon.lemonban.LoginActivity"); androidDriver.startActivity(activity);
注意:如找不到startActivity方法,请确认你的eclipse java compiler是否设置的是JDK1.8
Appium常用API2:得到当前页面的dom结构(getPageSource)
可以用于断言当前页面是否有某个元素,或者判断当前页面有没有产生变化,比如上下滚动判断是否已经到了底端或者顶端。
String pageSource = androidDrivr.getPageSource();
System.out.println(pageSource);
Appium常用API3:得到当前页面的类名(currentActivity)
String currentActivityName = androidDrivr.currentActivity();
System.out.println(currentActivityName);
Appium常用API4:重置应用的数据(resetApp)
有些场景我们需要清除应用的数据,相当于第一次安装时候的状态,比如:第一次启动app的引导页,登录等
androidDriver.resetApp();
Appium常用API5:判断App是否安装(isAppInstalled)
androidDriver.isAppInstalled("应用程序的包名");
Appium常用API6:向系统发送键值事件(pressKey)
Android平台独有,向系统发送键值事件,不同的键值对应不同的功能,如keyevent(4)表示手机的HOME按键
// 1 创建keyEvent对象 KeyEvent keyEvent = new keyEvent(); // 2 使用withKey传入键值 keyEvent.withKey(AndroidKey.VOLUME_UP); // 3 使用pressKey发送键值 androidDriver.pressKey(keyEvent);
Appium常用API7:截图功能
使用场景:当测试用例执行失败之后屏幕截图,保存到本地为了更好的查找问题
File file = androidDriver.getScreenshotAs(OutputType.FILE); FileUtils.copyFile(file, new File("D:\AppAuto\test.png"));
Appium常用API8:获取设备时间信息(getDeviceTime)
返回String类型
Appium常用API9:获取设备DPI(getDisplayDensity)
返回String类型,注意不是分辨率
Appium常用API10:获取automation name(getAutomationName)
默认为null,如果有指定automation name为uiautomator2就为对应的值
Appium常用API11:获取设备横竖屏状态(getOrientation)
有竖屏(PORTRAIT)和横屏(LANDSCAPE)
【root状态的Hybrid应用自动化测试介绍】
Appium提供的解决方案:基于UIAutomator+ChromeDriver
准备工作:
1 准备Android 4.4+的手机或者模拟器
2 在app源码中将webview调试模式打开webview.setWebContentsDebuggingEnabled(true)
如果是第三方线上app,一般webview debug开关都是关闭的,这就需要借助第三方工具,才能将debug开关打开
解决方案:(Root状态 )
Xposed.apk+WebviewDebugHook.apk装入模拟器或者是手机上
Xposed是一个框架,能够集成很多功能模块,这些模块能够在不修改APK的情况下,修改APP的运行方式。
安装好Xposed打开显示没有激活,需要在official菜单中选择安装,安装前需要选择适应的框架,如果是模拟器一般是x86框架,如果是真机需要选择arm框架。
需要WebviewDebugHook模块来开启APP的WebView debug模式 。
安装并激活好Xposed之后将WebviewDebugHook.apk放入模拟器或者手机,注意此时不会提示安装成功,需要在Xposed中找到“模块”菜单,找到WebviewDebugHook并勾选上,然后重启设备就会生效。
3 在电脑上安装UC开发者工具(uc-devtools.msi,设置成“本地Devtools Inspector UI资源”)
UC-Dectools工具的使用需要上面两步配置的支持才能使用,当模拟器或者手机上的当前界面是webView时UC-Dectools工具会检测到,点击UC-Dectools工具上面的“inspect”按钮进入到UI定位界面。
【Hybrid应用自动化脚本编写】
# 获取所有的上下文contexts
driver.getContextHandles();
# 切换到对应的上下文context
driver.context(webview视图对应的上下文);
# 定位webview中的元素,并执行操作
web网页元素定位和操作,利用Appium提供的解决方案:基于UIAutomator+ChromeDriver,进行webview中元素的定位
# 切换回默认的视图
driver.context(native视图);
示例:
// 1 进入到web页面中 androidDriver.findElementByAndroidUIAutomator("new UiSelector().text("网页版")").click(); // 2 获取所有的contexts Set<String> contexts = androidDriver.getContextHandles(); System.out.printIn(contexts); // 3 切换到webview所对应的上下文 androidDriver.context("WEBVIEW_COM.wuba"); // 4 找到网页中的“价格区间”按钮 androidDriver.findElementByXPath("//li[@data-id='p25-30']").click(); // 5 点击网页中的“确认”按钮 androidDriver.findElementByXPath("//a[text()='确认']").click();
【注意】在上面的切换到webview所对应的上下文操作中可能会遇上因为ChromeDriver和webView的版本不匹配所导致的错误。简单来说就是,ChromeDriver是appium中的一个工具,app中使用的webview有自己的版本号,ChromeDriver每个版本对webview的可支持版本都不一样,所以需要保证版本的可支持性。
1 查看ChromeDriver版本号,mac电脑的appium中ChromeDriver所在路径为:
/Applications/Appium.app/Contents/Resources/app/node_modules/appium/node_modules/appium-chromedriver/chromedriver
双击会终端运行ChromeDriver会显示出版本号,如下图所示
2 查看模拟器或者手机的webview版本号,有三种方式:
1)在模拟器或者真机上打开一款需要webview的app,然后进入到设置-应用程序中打开“显示系统进程”,找到类似下图所示的进程:
然后点击进去便可查看到版本号:
2)通过UC-Dectools工具中检测到的webview时,信息上会附带有版本信息。
3)使用adb工具,首先确保连接上夜神模拟器
$adb connect 127.0.0.1:62001
可以通过下面的命令确保是否连接成功:
$adb devices
然后查看系统的所有程序:
$adb shell pm list package -s
找到webview:
复制好此包名,然后查看该应用包的信息:
adb shell dumpsys package com.google.android.webview
最后找到关于version的相关信息:
3 第三步对照版本匹配表,在github可以查询到淘宝的镜像网站:
github上appium【https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/web/chromedriver.md 】
淘宝的镜像网站【http://npm.taobao.org/mirrors/chromedriver】
我电脑上的appium中的ChromeDriver版本号通过上面方式的查询是2.38版本,在镜像网站上找到对应的2.38目录,点击进去长相如下:
前面三个是安装包,点击最后一个文件“notes.txt”查看该ChromeDriver版本支持哪些webview版本:
4 如果版本不匹配,替换掉appium中的ChromeDriver应用程序。
比如本文所示,通过查看ChromeDriver的版本是2.38版本,支持的webview是65-67版本;通过查看webview的版本是75,存在不匹配的情况,因为webview的版本不能更改,因此只能替换掉appium中的ChromeDriver应用程序。
【非root状态的Hybrid应用自动化测试解决方案】
解决方案:通过VirtualXposed完成线上App开启webview调试,VirtualXposed相当于一个“应用分身”提供一个虚拟环境,这个虚拟环境中默认装了一个Xposed,
1 将VirtualXpose.apk安装到真机中,在VirtualXposed中安装WebviewDebugHook.apk和需要测试的app安装包
2 在VirtualXposed中打开测试的目标app,就相当于在root状态下测试app