Appium自动化测试框架
Posted 大忽悠爱忽悠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Appium自动化测试框架相关的知识,希望对你有一定的参考价值。
Appium自动化测试框架
环境搭建
- JDK环境安装(不介绍,自己百度)
- 安卓SDK环境安装
- 安卓SDK可以直接从下面的镜像连接进行下载
http://110.40.155.17/download/
把SDK下的这两个目录位置加入PATH环境变量即可
D:\\SDK\\androidsdk\\platform-tools
D:\\SDK\\androidsdk\\tools
- 安装Appium-DeskTop
- 手机模拟器下载夜神模拟器
为了防止可能存在的SDK冲突,建议将夜神模拟器安装目录下的nx-adb.exe替换为我们安装的SDK目录的adb.exe
adb
ADB全名Android Debug Bridge,是一个调试工具
构成和原理
- Client端 发送adb命令的电脑
- Daemon守护进程,安卓手机中接受adb命令的
- Server端 手机中管理通信的
工作原理
- client发送命令给server
- server将命令交给Daemon
- daemon执行命令
- 执行结果返回给server端
- 执行结果发送给client端
abd工具可以在电脑通过终端命令来操作安卓手机/模拟器
连接
- 连接某台手机或者模拟器(夜神模拟器程序暴露连接端口为620001)
adb connect 127.0.0,1:62001
- adb devices : 检测到连接到电脑的安卓设备
包名,界面名
自动化测试需要通过代码的形式告诉手机测试那个应用程序的哪一个界面,所以需要通过某种方式定位到某个应用程序的某个页面。
- 包名(package):决定程序的唯一性—可以用来定位到某个应用程序
- 界面名(activity): 一个界面名通常对应一个界面,界面名又被叫做启动名
获取包名和界面名的命令:
- Mac/Linux
adb shell dumpsys window windows | grep mFocusedApp
- Windows
adb shell dumpsys window windows | findstr mFocusedApp
获取当前手机上打开正在显示的那个应用程序的包名和界面名
mFocusedApp=AppWindowToken43fda26 token=Token51a168 ActivityRecordffecc8b u0 com.android.settings/.Settings t3
- 包名: com.android.settings
- 界面名(启动名):.Settings
文件传输
- 发送数据到手机
adb push 电脑文件路径 手机文件夹路径
C:\\Users\\zdh\\Desktop\\stu.sql
- 将某个.txt文件发送到手机的SDK卡,SDK卡的根目录名固定为/sdcard
- 从手机获得数据
adb pull 手机的文件路径 电脑的文件夹路径
例如:
adb pull /sdcard/stu.sql C:\\Users\\hhh
获取APP启动时间
adb shell am start -W 包名/启动名(界面名)
- 命令作用: 启动对应的程序,并进入指定的界面
例如: 我们点击设置程序,看看它的启动时间(我们只需要执行下面的命令,该命令会发送给对应的手机和模拟器,然后对应会去打开设置应用程序,记录相关时间参数后,返回给我们)
adb shell am start -W com.android.settings/.Settings
参数解释:
- ThisTime: 该界面(activity)启动耗时(毫秒)
- TotalTime:应用自身启动耗时=ThisTime+应用application等资源启动时间(毫秒)
- WaitTime:系统启动应用耗时=TotalTime+系统资源启动时间(毫秒)
获取手机日志
adb logcat
- 使用上面命令后,会实时监控手机运行时输出的日志信息
- 在调试程序的时候,通过实时监控日志信息,可以找到错误日志,然后记录下来
安装和卸载APP
安装app到手机
adb install 路径/xx,apk
卸载手机上的app,需要指定包名--获取应用程序包名的方法上面给出了
adb uninstall 包名
进入到安卓手机内部的Linux系统命令行
adb shell
启动和停止adb服务端
- 启动adb服务端,出bug时使用可以重启服务器,先关闭再启动
adb start-server
- 停止adb服务端
adb kill-server
查看命令帮助
adb --help
其他命令
Appium
save as Preset是保存当前预设配置
inspector工具
填写完毕后,先保存配置,然后点击start session开启会话即可
元素探测
appium提供的元素探测
原生的探测工具
包名和类名
查询当前聚焦的页面类名
adb shell dumpsys window windows | findstr mFocusedApp
aapt命令
需要先进入sdk安装目录的build-tools目录下
- 查询包名
aapt dump badging 应用apk的路径 | findstr package
- 查询类名
aapt dump badging 应用apk的路径 | findstr launchable-activity
APP类型
APP布局和控件
JAVA SDK
- 引入依赖
<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为案例
- 建议通过原生SDK拍摄快照的方式来定位元素的id,这样会快很多
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);
配合Testng框架使用
Appium自动化原理
对于安卓应用来说,Appium会往对应安卓手机上推送一个Bootstrap.jar并运行它,当我们自动化测试程序向appium发送请求时,appium向Boostrap.jar发送请求,由Bootstrap.jar转发请求到安卓手机底层的自动化测试框架UIAutomator。
再由底至上,将测试结果最终返回给我们的测试程序。
初始化流程
通过appium初始化日志分析得到
- 获取配置相关参数
- 相关环境检查—jdk版本,连接的设备,adb配置,设备的sdk版本
- 检查对应的应用程序是否安装,并且判断设备是否可以正常连接
- 将自动辅助程序appium.setting推送到手机端,如果已经安装就不管了
- 获取手机相关信息,屏幕尺寸,品牌等
- 检查对应包名是否已经安装好了,如果安装好了,如果该应用程序在执行,会停止掉
- 清空应用程序数据—shell pm clear 包名
- 设置端口转发,推送Bootstrap.jat包到手机端,然后启动
- 启动应用程序
元素定位
ID定位
//如果resource-id唯一,那么使用下面这个方法就行
//如果存在多个元素resource-id相同,那么下面api默认选择第一个
androidDriver.findElementById();
//如果存在多个元素resource-id相同,使用下面api---返回一个list
androidDriver.findElementsById();
text定位
- Appium 1.5之前版本支持By.name方式
androidDriver.findElement(By.name(""));
- 最新版本Appium不再支持此API,需要使用UIAutomator原生自动化引擎
androidDriver.findElementByAndroidUIAutomator("new UiSelector().text('登录')");
className定位
根据class属性去找元素,一般在页面中很多元素的class属性都是一致的,所以这种方式基本不用。
xpath定位
- xpath有相对定位和绝对定位,注意区分
//使用相对定位---//开头
androidDriver.findElementByXPath("//android.widget.Button[@text='登录']").click();
accessibility id定位
在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)会在特定的超时时间内重试多次寻找控件
- 在设定的时间范围内,每间隔设定的轮询时间定位指定元素,每次间隔的轮询时间内没有定位成功会忽略异常,如果超出设定时间仍未定位成功则抛出异常
- 可以使用ExpectedConditions中的多种方法来满足不同的定位需求
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();
- driver: appiumdriver的初始化实例
- timeout:查询条件的最大等待时间,Duration.ofSeconds(10)代表最大的等待时间10秒
- sleep:设置查询条件的时间频率,Duration.ofSeconds(1)代表每间隔1秒去定位元素
- ignoring:如果每间隔1秒定位元素失败则忽略对应的异常
- wait.until:WebDriverWait需要和until方法结合使用,通过调用ExpectedConditions里面的方法来返回你想要的值
手势操作—滑动
- java-client 6.0版本之后的使用方式
/**
* @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().windowappium自动化测试 电脑无线连接手机