• App自动化测试_Python+Appium使用手册


    一、Appium的介绍

    Appium是一款开源的自动化测试工具,支持模拟器和真机上的原生应用、混合应用、Web应用;基于Selenium二次开发,Appium支持Selenium WebDriver支持的所有语言(java、 Object-C JavaScript 、p hp、 Python等,Appium支持任何一种测试框架,

    二、Appium架构

    Appium 是一个用Node.js编写的HTTP server,它创建、并管理多个 WebDriver sessions 来和不同平台交互,如 iOS ,Android等等. 

    Appium 开始一个测试后,就会在被测设备(手机)上启动一个 server ,监听来自 Appium server的指令. 每种平台像 iOS 和Android都有不同的运行、和交互方式。所以Appium会用某个桩程序“侵入”该平台,并接受指令,来完成测试用例的运行

    三、Appium的工作原理

    Client端发送自动化指令给Appium server,Appium Server接收到client发送的指令后,转换为移动端能够识别的ADB指令,然后发送给移动端设备,并对移动端设备进行操作。
    【工作过程】
    脚本请求 --> 4723端口appium server —> 解析参数给PC端4724端口 —> 发送给设备4724端口 —> 通过设备4724端口发给bootstrap.jar —> Bootstrap.jar把命令发给uiautomator
    注意: Bootstrap.jar :是 push Android 手机上的一个应用程序,主要是接受 Appium Server 的执行并 运行这些测试指令。而指令的执行正是通过UIAutomator 来驱动的。

    三、Appium环境搭建

    使用appium服务需要配置依赖环境,以及各个环境之间的版本需要互相兼容; 

    我目前使用的版本信息: 

    # JDK版本

    jdk-8u261
    # android SDK版本
    installer_r24.4.1-windows.exe
    # appium客户端
    Appium-windows-1.15.1.exe
    # Appium-Python-Client
    pip install Appium-Python-Client==2.11.1

    # selenium版本

    selenium==4.21.0
    # 模拟器
    nox_setup_v7.0.2.7_full.exe

    网盘获取:

    链接:https://pan.baidu.com/s/18uqZfmX9d9HqqSBIEM73uA?pwd=z5ak 
    提取码:z5ak 

    3.1、配置jdk环境

    详见JDK配置文章:

    Windows配置java环境JDK-CSDN博客

    3.2、android SDK环境

    详见SDK配置文章:

    Android SDK下载安装(_指定版本)-CSDN博客

    3.3、安装appium客户端

    Appium安装除了安装Appium客户端,还要在Python环境中安装Appium-Python-Client,其作用就是将 Python 与 appium 关联起来;

    需要指定版本:pip install Appium-Python-Client==2.11.1

    详见Appium配置文章:

    Appium安装及配置(Windows环境)-CSDN博客

    3.5、下载模拟器

    详见模拟器配置文章:

    Android模拟器下载及配置_夜神模拟器-CSDN博客

    四、启动App

    操作步骤:

    • 启动桌面Appium客户端
    • 启动模拟器或移动设施(确保连接adb)
    • 启动Python脚本

    4.1、启动桌面Appium客户端

    双击 桌面Appium客户端-->启动服务器

    4.2、启动模拟器或移动设施(确保连接adb)

    ADB命令:ADB日常使用命令-CSDN博客

    启动模拟器后,一般默认会已经连接adb,如果没有连接需要输入adb命令进行连接

    1. # 验证是否连接成功
    2. adb devices
    3. # 建立连接
    4. adb connect 127.0.1: 模拟器端口号〈逍遥模拟器21503〉

    已连接 

    4.3、运行Python脚本,启动App

    启动参数:

    1. # 1.导入appium中的webdriver
    2. from appium import webdriver
    3. import time
    4. def startUp():
    5. print('准备启动app')
    6. # 2. 启动参数,配置手机连接的参数内容,
    7. # 所有参数信息都是键值对的方式进行连接
    8. desire_caps = {
    9. "deviceName": "127.0.0.1:62001", # 参数1:当前设备的名称
    10. "platformName": "Android", # 参数2:系统
    11. "platformVersion": "7.1.2", # 参数3:系统版本号
    12. "appPackage": "com.android.contacts", # 参数4:启动的app名称(包名)
    13. "appActivity": ".activities.PeopleActivity t12", # 参数5:app界面名称
    14. "noReset": True,
    15. "unicodeKeyboard": True
    16. }
    17. # 3.发送连接请求
    18. driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_capabilities=desire_caps)
    19. # 等待时间,防止启动后立马关闭
    20. time.sleep(4)
    21. # 关闭驱动
    22. driver.quit()
    23. if __name__ == '__main__':
    24. startUp()

    启动参数说明

    待补充

    五、元素定位工具

    Android SDK 自带的一个工具,在 sdk tools 目录下(一定关闭 appium-desktop 的自带的定位工具后打 开,否则连接不上模拟器/ 真机 )
    找到 sdk 下的 tools 里面的 uiautomatorviewer.bat

    详见uiautomatorviewer.bat使用文章:

    appium元素定位工具_uiautomatorviewer.bat-CSDN博客

    六、元素定位

    6.1、单个元素定位:find_element

    语法:

    # 需要导包

    from selenium.webdriver.common.by import By

    # 用法  --传递两个参数:一个是定位属性,一个是该属性的值

    driver.find_element(By. 属性 ,' 属性值 ').操作方法 
    1.通过id定位
    resource-id 的值为id
    driver . find_element ( By . ID , " xxxx " )
    2.通过class_name定位
    # 取class的属性值
    driver . find_element ( By . CLASS_NAME , " xxxx " )
    3.通过link_text定位
    # 取 text 属性值--文本属性
    driver . find_element ( By . LINK_TEXT , " xxxx " )
    4.通过xpath定位
    # 取元素xpath路径
    driver . find_element ( By . XPATH , " xxxx " )

    # 使用文本属性比较实用(推荐)

    driver.find_element(By.XPATH, '//*[@text="显示"]').click()
    driver.find_element(By.XPATH, '//*[@text="亮度"]').click()

    5.通过坐标定位driver.tap()

    通过坐标定位有一定的局限性

    • 优先:任意的元素都可以通过坐标进行定位操作

    • 缺点:当手机的分辨率发生变化的时候,元素的坐标值也会发生变化

    注意点:通过坐标定位的方法,一般使用场景是固定的机型或者固定的分辨率下进行自动化测试

     driver.tap()

    左边通过[(x,y)]形式传入

    duration:表示触碰时间/ms

    # 左上角
    driver.tap([(144,1327)], duration=100)  
    # 右下角
    driver.tap([(208, 1370)], duration=100)  

    6.2、多个元素定位find_elements

    find_element 方式基本一致,这个方法可以同时定位多个元素,返回一个列表
    例如:通过 ID 定位到多个元素,我想点击第一个元素
    driver . find_elements ( By . ID , "xxxxx" )[ 0 ]. click ()
    #
    list1 = driver . find_elements ( By . ID , "xxxxx" )
    list1 [ 0 ]. click ()

    七、元素操作方法

    Appium元素的操作方法与Selenium元素操作方法,基本一样

    1. click():点击

    1. # 模拟鼠标点击操作
    2. driver.find_element(By.ID," xxxx ").click()

    2. clear():清空

    1. # 清空元素输入框中内容
    2. driver.find_element(By.ID," xxxx ").clear()

    3. send_keys():输入

    1. # 往输入框中,输入内容值
    2. driver.find_element(By.ID," xxxx ").send_keys("输入的内容")

    4. text:获取文本属性值

    1. # 获得元素的text内容
    2. result = driver.find_element(By.XPATH," xxxx").text
    3. print(result)

    5. get_attribute():获取某个标签,元素的属性

    1. # 获取标签xxx的元素的 class 属性值
    2. driver.find_element(By.ID,'xxx').get_attribute('class')

    6. size:获取元素的大小(宽、高)

    1. # 获取元素的大小,得到一个字典如:{'height': 48, 'width': 640}
    2. driver.find_element(By.ID,' xxxx ').size

    7. location:获取元素的坐标

    1. # 获取元素的坐标,得到的是一个字典{}
    2. res = driver.find_element(By.ID,' xxxx ').location
    3. print(res) # {'x': 108, 'y': 380}

    8. is_selected():判断元素是否被选中

    1. # 选中返回True,否则返回False
    2. res = driver.find_element(By.ID, 'xxxx').is_selected()
    3. print(res) # True/False

    9. is_enabled():判断元素是否被启用

    1. # 判断元素是否被启用,返回True / False
    2. res = driver.find_element(By.ID, 'xxxx').is_enabled()
    3. print(res)

    10. is_displayed():判断元素是否显示

    1. #判断元素是否显示,返回True / False
    2. res = driver.find_element(By.ID, 'xxxx').is_displayed()
    3. print(res)

    八、其它操作

    8.1、操作应用包

    1、判断是否安装

    1. # 语法 返回True/False
    2. driver.is_app_installed("应用的包名")
    3. # 可以简单做一个判断,然后进行安装或卸载
    4. if driver.is_app_installed('io.manong.developerdaily'):
    5. driver.remove_app('io.manong.developerdaily')
    6. print('app卸载成功')
    7. else:
    8. driver.install_app(r'D:\MS_App_auto_24_529\toutiao.apk')
    9. print('app安装成功')

    2、安装应用

    install_app("app的绝对路径")
    ​​​​ 3、卸载应用
    remove_app("应用的包名")

     4、获取当前操作的应用的界面名称

    current_activity

    5、获取包名
    driver.current_package

    8.2、操作屏幕页面

     1.获取屏幕尺寸

    方法:driver.get_window_size

    1. # 得到的是一个字典,width是屏幕的宽度,height为屏幕的高度
    2. dic = driver.get_window_size()
    3. print(dic ) #{'width': 1080, 'height': 1776}
    2.页面滑动
    app界面为了保证内存优化性,所有可操作的元素只是当前界面存在的元素,所以需要滑动屏幕,进行元素定位
    方式1:坐标定位.swipe()
    方法 driver.swipe(x1, y1, x2, y2,duration)
    参数
    (x1, y1):开始坐标,(x2, y2):结束坐标,duration:滑动时间
    注意 :滑动的坐标不能超过屏幕的宽高
    实现思路1:通过  driver.get_window_size()  获得窗口高和宽 -->按比例滚动
    1. # 获取屏幕尺寸得到所用坐标
    2. def getSize(driver):
    3. x = driver.get_window_size()['width']
    4. y = driver.get_window_size()['height']
    5. x1 = x * 0.75
    6. x2 = x * 0.25
    7. y1 = y * 0.75
    8. y2 = y * 0.25
    9. return x1, y1, x2, y2
    10. # 完成上滑操作
    11. def swipeUp(driver):
    12. x1, y1, x2, y2 = getSize(driver)
    13. driver.swipe(x1, y1, x1, y2, 100)

    实现思路2:获取两个元素的坐标,在掉用swipe方法,从a元素 滚动到 b元素

    1. # 获取元素坐标
    2. a_dic = driver.find_element(By.XPATH,'//*[@text="WLAN"]').location # {'x': 144, 'y': 799}
    3. b_dic = driver.find_element(By.XPATH,'//*[@text="通知"]').location # {'x': 144, 'y': 1471}
    4. # 将通知 滚动到 WLAN
    5. driver.swipe(b_dic["x"], b_dic["y"], a_dic["x"], a_dic["y"], duration=100)
    方式2: 通过元素的相对位置进行滚动
    1. # 页面滚动
    2. el1 = driver.find_element(By.XPATH, '//*[@text="显示"]')
    3. el2 = driver.find_element(By.XPATH, '//*[@text="WLAN"]')
    4. # 将"显示" 滚动到 "WLAN"位置
    5. driver.scroll(el1, el2)

    如图所示,将下面的“显示”滑动到上面的“WLAN”位置 

    3.获取界面的xml源码
    方法 driver.page_source()
    用法 :可以用来断言或检查
    1. page = driver.page_source()
    2. assert '登陆成功' in page
    4.拖拽操作 driver.drag_and_drop()
    语法:driver.drag_and_drop(el1, el2)  --元素1:el1,元素2:el2
    作用:将el1元素,拖拽到el2
    1. # 拖拽操作
    2. el1 = driver.find_element(By.XPATH, '//*[@text="浏览器"]')
    3. el2 = driver.find_element(By.XPATH, '//*[@text="开发者头条"]')
    4. driver.drag_and_drop(el1, el2)

    5、事件链TouchAction

    作用:构建相对比较复杂的,连续的触摸行为

    使用步骤
    通过" TouchAction" 类,创建事件链对象,然后传入 driver对象
    通过添加各种方法完成事件
    • 按下
    • 长按
    • 移动
    • 等待
    • 松手
    • 轻敲
    • ...
    代码示例:
    1. # 事件链操作TouchAction
    2. # 导包
    3. from appium.webdriver.common.touch_action import TouchAction
    4. # 实例化对象
    5. action = TouchAction(driver)
    6. time.sleep(1)
    7. # 定位元素
    8. ele1 = driver.find_element(By.XPATH, "//*[@text='通知']")
    9. ele2 = driver.find_element(By.XPATH, "//*[@text='WLAN']")
    10. # 拖拽操作,将ele2,拖到 ele1位置
    11. # press:按下操作 .wait(100):按下持续时间 move_to(ele1):移动到目标元素或坐标
    12. action.press(ele1).wait(1000).move_to(ele2)
    13. # 完成后,松手操作 release():松手动作
    14. action.release()
    15. # 提交事件链,生效
    16. action.perform()
    17. # 再次滑动
    18. ele3 = driver.find_element(By.XPATH, "//*[@text='电池']")
    19. action.press(ele3).wait().move_to(ele1)
    20. # 完成后,松手操作 release():松手动作
    21. action.release()
    22. # 提交事件链,生效
    23. action.perform()
    24. # 点击安全
    25. ele4 = driver.find_element(By.XPATH, "//*[@text='安全']")
    26. ele4.click()
    27. time.sleep(0.5)
    28. # 通过事件链,绘制锁屏图案
    29. # 点击屏幕锁定
    30. driver.find_element(By.XPATH, "//*[@text='屏幕锁定']").click()
    31. time.sleep(0.5)
    32. # 点击图案
    33. driver.find_element(By.XPATH, "//*[@text='图案']").click()
    34. time.sleep(2)
    35. # 拖拽绘制图案
    36. action.press(x=182, y=775)\
    37. .wait(100).move_to(x=449, y=1044) \
    38. .wait(100).move_to(x=190, y=1311) \
    39. .wait(100).move_to(x=714, y=1299)

    6、截图/截屏
    1. driver.get_screenshot_as_file('网络情况.png')
    2. time.sleep(1)
    3. driver.save_screenshot('网络情况2.png')
    7、其它操作
    重置 app
    方法 driver.reset()
    用法 :相当于卸载重装应用,所以本地缓存会失效。
    模拟摇晃设备
    方法 driver.shake
    退出键盘
    方法 driver.hide_keyboard()
    app 置于后台运行 x
    方法 driver.background_app(x)
    用法 x 为置于后台的秒数
    模拟系统按键
    方法 :driver.press_keycode(KEYCODE_BACK)
    用法 :根据 AndroidkeyCode 来完成指定操作
    键名
    描述  
    键值
    KEYCODE_CALL
    拨号键
    5
    KEYCODE_ENDCALL
    挂机键
    6
    KEYCODE_HOME
    按键 Home
    3
    KEYCODE_MENU
    菜单键
    82
    KEYCODE_BACK
    返回键
    4
    KEYCODE_SEARCH
    搜索键
    84
    KEYCODE_CAMERA
    拍照键
    27
    KEYCODE_FOCUS
    拍照对焦键
    80
    KEYCODE_POWER
    电源键
    2
    获取手机的网络信息
    # - 获取手机的网络信息:网络信息三种模式分别对应数值
    # 飞行模式: 1
    # 无线网络: 2
    # 移动网络: 4
    # 网络情况可以进行组合: 3=1+2,6=2+4
    print(driver.network_connection)
    # - 设置手机网络信息
    1. driver.set_network_connection(2)
    2. time.sleep(1)
    3. print(driver.network_connection)
    # - 获取当前手机时间
    print(driver.device_time)
    # - 打开通知栏
    driver.open_notifications()

    九、时间等待

    待补充

    十、APP自动化框架设计思想

    参考文档:

    Appium使用教程_Android篇-CSDN博客

  • 相关阅读:
    Docker上部署mysql(超简单!!!)
    C++11标准模板(STL)- 算法(std::merge)
    parallel recovery slave next change & parallel recovery push change
    14.(开发工具篇github)如何在Github配置ssh key
    Java面试题之 mysql面试题
    Day58 739. 每日温度 496.下一个更大元素 I
    用于肺结节分类的常规 EHR 的纵向多模态Transformer集成成像和潜在临床特征
    7、MySQL——聚合函数、分组查询、HAVING和WHERE子句的区别、LIMIT、分页查询
    5.1异常处理
    【框架学习 | 第六篇】SpringBoot基础篇(快速入门、自动配置原理分析、配置文件、整合第三方技术、拦截器、文件上传/下载、访问静态资源)
  • 原文地址:https://blog.csdn.net/2401_83004375/article/details/139328068