手机端自动化
环境:
appium+genymotion(模拟器)+安卓SDK(uiautomatorviewer)+fiddler(抓包)
步骤:
·adb构成:
-client端:在电脑上,负责发送adb命令
-daemon守护进程:在手机上,负责接收和执行adb命令
-server端:在电脑上,负责管理client和daemon之间的通信
·adb的工作原理:
1)client端将命令发送给server端
2)server端将命令发送给daemon端
3)daemon进行执行
4)将执行结果发回给server端
5)server端将结果返回给client端
·adb常用命令
1)包名(package):决定程序的唯一性
2)界面名/启动名(activity):一个界面名对应一个界面
3)使用步骤:
i)打开需要测试的应用程序
ii)输入adb命令:adb shell dumpsys window windows | findstr mFocusedApp
结果:mFocusedApp=AppWindowToken{23bd9063 token=Token{3b577e92 ActivityRecord{2aae9c1d u0 com.android.settings/.SubSettings t4}}}
/左边一直到前一个空格是包名:com.android.settings
/右边一直到后一个空格是界面名:.SubSettings
·文件传输:
1)发送文件至手机——>adb push 电脑的文件路径 手机的文件夹路径:adb push C:\Users\22648\Desktop\a.txt /sdcard
2)从手机当中拉取文件——>adb pull 手机的文件夹路径 电脑的文件路径:adb pull /sdcard/a.txt C:\Users\22648\Desktop
·获取APP的启动时间:参照标准——不超过同类产品的一倍
ThisTime: 该界面(activity)adb shell am start -W com.android.settings/.SubSettings启动耗时(毫秒)
TotalTime: 应用自身启动耗时
WaitTime: 系统启动应用耗时
·获取手机日志【应用】:将bug日志信息发送给开发人员,便于开发人员定位bug
使用步骤:
1)打开需要测试的应用程序
2)找到触发bug的位置
3)使用查看日志命令:adb logcat——错误奔溃会有E/ at
4)触发bug
5)获取日志信息
命令:adb logcat
应用场景:当程序发生崩溃时,我们可以将日志信息发送给开发人员,便于快速定位bug
关于奔溃的处理:需要找到日志中“at”前面的第一个字符时E的就是错误信息
·其他命令:
adb install 路径/xx.apk 安装APP到手机
adb uninstall 包名 卸载手机上的APP,需要指定包名
adb devices 获取当前电脑已经连接设备和对应的设备号
adb shell 进去到安卓内部的Linux系统命令行中
adb start-server 启动adb服务端,出bug时可以使用重启服务器,先关闭再启动
adb kill-server 停止adb服务端,出bug时使用可以重启服务器,先关闭再启动
adb --help 查看adb帮助
·如何使用appium打开任意一个应用程序
前置代码:
desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.167.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
desired_caps['unicodeKeyboard'] = True
desired_caps['resetKeyboard'] = True
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) # appium的地址
步骤:
1.打开要测的应用
2.使用adb命令获取包名和界面名
3.修改desire_caps字典中的appPackage和appActivity的参数
·如何通过代码跳转至其他的APP
应用场景:如果一个应用需要跳转到另一个应用,可以使用这个api进行应用的跳转,就像我们通过外卖应用下订单之后会跳转到支付应用一样
方法:driver.start_activity(app_package='xxx', app_activity='xxx')
·如何通过代码获取APP的包名和界面名:
获取包名:driver.current_package
获取界面名:driver.current_activity
·关闭APP和驱动对象
关闭当前操作的APP,不会关闭驱动对象
driver.close_app()
关闭驱动对象,同时关闭所有关联的APP
driver.quit()
·安装和下载以及是否安装APP
driver.install_app(app_path) # app_path:apk路径
driver.remove_app(app_id) # app_id:应用程序包名
driver.is_app_install(app_id) # 结果为布尔类型,True为安装,False为没有安装
if driver.is_app_install(app_id):
driver.remove_app(app_id)
else:
driver.install_app(app_path)
·将应用置于后台
应用场景:银行类APP会在进入后台一定时间后,如果再回到前台页面也需要重新输入密码,如果需要自动化测试这种功能,可以使用这个api进行测试
方法:
driver.background_app(seconds)
注意点:
热启动:表示进入后台回到前台
冷启动:关机再开这种切断电源的行为
·UIAutomatorViewer的使用:
1)进入安卓的SDK目录下,在tools目录下,打开uiautomatorviewer.bat
2)电脑连接真机或打开安卓模拟器
3)启动待测试APP(保证想要查看的元素在当前的屏幕上)
4)点击uiautomatorviewer的左上角的Device Screenshot(从左数第二个按钮)
5)点击需要查看的控件
6)查看有效较Node Detail相关信息
注意点:
命令行窗口不要关闭
如果uiautomatorviewer闪退,更换jdk为1.8
点击第二个按钮时报错:
重启adb
adb kill-server
adb start-server
·元素定位操作API:
应用场景:计算机不像人一样聪明,我们需要通过元素定位来获取元素,才能让计算机帮我们操作这个元素
步骤:
1)打开uiautomatorviewer工具
2)打开模拟器或真机
3)通过uiautomatorviewer工具获取想要进行操作的元素的Node Detail信息
4)通过元素定位API进行定位(id、xpath、class)
5)对元素进行相关操作
定位一个元素:
driver.find_element_by_id(id_value) id_value:元素的resource_id的属性值
driver.find_element_by_class_name(class_value)
driver.find_element_by_xpath(xpath_value)
定位一组元素:
driver.find_elements_by_id(id_value) id_value:元素的resource_id的属性值
driver.find_elements_by_class_name(class_value)
driver.find_elements_by_xpath(xpath_value)
例子:
titles = driver.find_elements_by_id('com.android.settings:id/title')
print(len(titles))
for title in titles:
print(title.text)
titles[1].click()
注意点:
元素的定位基于当前屏幕范围内展示的可见元素
元素的id/class_name重复时,定位到的是页面从上至下的第一个元素
例子:
# 通过xpath的形式,获取所有包含“S”的元素,并打印其文字内容
# eles = driver.find_elements_by_xpath("//*[contains(@text, 'S')]") # contains是包含
# print(len(eles))
# for i in eles:
# print(i.text)
·定位元素的注意点:
driver.find_element_by_id(id_value),id_value不存在时,会报错——NoSuchElementException
driver.find_elements_by_id(id_value),id_value不存在时,会返回一个空列表
·元素等待
应用场景:可能由于一些原因,我们想找的元素并没有立刻出来,此时如果直接定位元素可能会报错,比如以下原因:
1)由于网络速度原因
2)服务器处理请求原因
3)电脑/手机配置原因
概念:找元素的时候,通过一个时间的设置,进行元素等待,等待元素出来之后,再进行定位,防止报错
分类:隐式等待和显式等待
隐式等待:
应用场景:针对所有定位元素的超时时间设置为同一个值的时候
概念:等待元素加载指定的时长,超出时长抛出NoSuchElementException的异常
语法:driver.implicitly_wait(seconds)
作用:在设置了超时时间之后,后续所有的定位元素的方法都会在这个时间内等待元素
如果出现了,直接进行后续操作
如果没有出现,报错。抛出NoSuchElementException
显式等待:
应用场景:针对所有定位元素的超时时间设置为不同的值的时候
概念:等待元素加载指定的时长,超出时长抛出TimeoutException的异常
语法:WebDriverWait(driver, timeout).until(lamda x: x.find_element_by_id('xxx')).click()
超时时间 等待xx元素 进行xx操作
隐式等待和显式等待的选择:
从使用的角度上:
隐式等待更简单
显式等待相对负责
从灵活性的角度上:
显示等待更加灵活,因为可以针对每一个元素进行单独的设置
隐式等待是针对去阿奴的定位元素
关于sleep的问题:
sleep不是不能做元素等待,而是不推荐,因为会造成时间上的浪费
从选择的角度:
考虑使用的是单个还是全局?
考虑灵活性的问题?
·元素操作的api
点击元素:element.click()
输入框输入文字:element.send_keys()
对输入框清空文字:element.clear()
获取元素的文本内容:element.text
获取元素的位置:element.location,是一个字典,有x和y两个key
获取元素的大小:element.size,是一个字典,有width和height两个key
根据属性名获取元素的属性值:element.get_attribute(value) # value:元素的属性
# 获取元素的属性值
titles = driver.find_elements_by_id('com.android.settings:id/title')
for title in titles:
# print(title.get_attribute('enabled'))
# print(title.get_attribute('clickable'))
# print(title.get_attribute('text'))
# print(title.get_attribute('resourceId'))
print(title.get_attribute('className'))
print(title.get_attribute('name')) # 返回content-desc/text属性值
·滑动和拖动事件
swipe滑动事件:从一个坐标位置滑动到另一坐标位置,只能是两个点之间的滑动
driver.swipe(100, 2000, 100, 1000, 5000) # 起点的X,Y——>终点的X,Y,持续时间(单位是毫秒)
小结:
距离相同时,持续时间越长,惯性越小
持续时间相同时,手指滑动的距离越大,实际滑动的距离也就越大
scroll滑动事件:从一个元素滑动到另一个元素,直到页面自动停止
driver.scroll(origin_el, destination_el) # origin_el:滑动开始的元素, destination_el:滑动结束的元素
可以设置持续时间,但是惯性很大
drag_and_drop:从一个元素拖拽到另一个元素,第二个元素替代第一个元素原本屏幕上的位置
driver.drag_and_drop(origin_el, destination_el) # origin_el:滑动开始的元素, destination_el:滑动结束的元素
不可以设置持续事件,没有惯性
滑动和拖拽事件的选择:
滑动和拖拽无非是考虑是否有“惯性”,以及传递的参数是“元素”还是“坐标”,可以分成以下四种情况:
有"惯性",传入元素:
scroll
无"惯性",传入元素:
drag_and_drop
有"惯性",传入坐标:
swipe,并且设置较短的duration时间
无"惯性",传入坐标:
swipe,并且设置较长的duration时间
·高级手势TouchAciton
使用步骤:
创建TouchAction对象
通过对象调用想执行的手势
通过perform()执行动作
轻敲:TouchAction(driver).tap(ele).perform():tag可传ele或x,y坐标,count是多次点击
按下:TouchAction(driver).press(ele).perform()
抬起:TouchAction(driver).release().perform()
等待:TouchAction(driver).wait(ms=0).perform()
长按:模拟手指对元素或坐标的长按操作,比如,长按某个按钮弹出菜单
TouchAction(driver).long_press(el,x, y, duration).perform()
长按<==>按下.等待.抬手
手指移动:比如收拾解锁需先按下屏幕,再移动
Touch_Action(driver).press(x, y).move_to(x, y).perform()
·手机操作API
1.获取手机分辨率:
driver.get_window_size()
2.获取手机截图
driver.get_screenshot_as_file(‘图片路径’)
3.获取和设置网络状态
应用场景:视频应用在使用流量看视频的时候,大部分会提示用户正在使用流量是否继续播放。作为测试认我,我们可能需要用自动化的形式来判断是否有对应的
提示。即用流量的时候应该有提示,不用流量的时候应该没有提示。
获取网络状态:driver.network_connection
结果有:1, 2, 4, 6——>可以查看源代码
设置网络:driver.set_network_connection(1)
判断网络状态:if driver.network_connection == ConnectionType.DATA_ONLY:
4.发送键到设备:模拟按“返回键”和“home键”等等操作,比如很多应用都要按两次返回键才能推出应用的功能,如果这个功能需要我们做自动化,那么一定会用到这个方法
driver.press_keycode(keycode, metastate=None) keycode:发送给设备的关键代码,metastate:关于被发送的关键代码的元信息
参考安卓的keycode:https://www.cnblogs.com/bluestorm/p/4886662.html
driver.press_keycode(keycode=25) 按音量减
5.操作手机通知栏:测试即时通信类软件的时候,如果A给B发送一条消息,B的通知栏肯定会显示对应的消息。我们想通过通知栏来判断B是否收到消息,一定要先操作手机的通知栏
driver.open_notifications()
appium官方并没有给我们提供关闭通知的API,那么现实生活中怎么关闭,就怎么关闭。比如,手指从上往下滑动或者按返回键
driver.press_keycode(4)

浙公网安备 33010602011771号