Getting started with mobile testing Appium

1. Appium

1. Run Appium.exe, start the inspector session

2. Select service: automatic setting, add parameters

  desired_caps常用参数:
    platformName         平台的名称:iOS, Android, or FirefoxOS
    platformVersion      设备系统版本号
    deviceName           设备号 IOS:instruments -s devices,Android: adb devices
    app                  安装文件路径:/abs/path/to/my.apk or http://myapp.com/app
    appActivity          启动的Activity
    appPackage           启动的包
    unicodeKeyboard      unicode设置(允许中文输入)
    resetKeyboard        键盘设置(允许中文输入)
  # server 启动参数
    desired_caps = {}
    desired_caps['platformName'] = 'Android' 
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True

(1) View deviceName

(2) Query the package name (appPackage) and startup item (appActivity) of the current application

方式一:
adb shell dumpsys window w |findstr \/ |findstr name=
方式二:
adb shell dumpsys window | findstr mCurrentFocus

3. Click to start the session to record the script 

 4. Example code:

# This sample code uses the Appium python client
# pip install Appium-Python-Client
# Then you can paste this into a file and simply run with Python

from appium import webdriver

caps = {}
caps["platformName"] = "Android"
caps["deviceName"] = "127.0.0.1:62001"
caps["appPackage"] = "com.android.settings"
caps["appActivity"] = "com.android.settings.Settings"

driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)

el1 = driver.find_element_by_accessibility_id("搜索")
el1.click()
el2 = driver.find_element_by_id("android:id/search_src_text")
el2.send_keys("123")
el3 = driver.find_element_by_accessibility_id("收起")
el3.click()

driver.quit()

 2. App basic operation API

2.1 Preamble code

from appium import webdriver

caps = {}
caps["platformName"] = "Android"
caps["deviceName"] = "127.0.0.1:62001"
caps["appPackage"] = "com.android.settings"
caps["appActivity"] = "com.android.settings.Settings"

driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)

2.2 Install APK to mobile phone

 driver.install_app(app_path) 
 参数:
     app_path:脚本机器中APK文件路径

2.3 Remove APP from mobile phone

driver.remove_app(app_id) 
参数:
    app_id:需要卸载的app包名

 2.4 Determine whether the APP has been installed

driver.is_app_installed(bundle_id) 
参数:
   bundle_id: 可以传入app包名,返回结果为True(已安装) / False(未安装)

 2.5 Send files to mobile phone

 import base64
 data = str(base64.b64encode(data.encode('utf-8')),'utf-8')
 driver.push_file(path,data)
 参数:
     path:手机设备上的路径(例如:/sdcard/a.txt)
     data:文件内数据,要求base64编码
     Python3.x中字符都为unicode编码,而b64encode函数的参数为byte类型,需要先转码;
     生成的数据为byte类型,需要将byte转换回去。

2.6 Pull files from the phone

import base64
data = driver.pull_file(path) # 返回数据为base64编码
print(str(base64.b64decode(data),'utf-8')) # base64解码
参数:
    path: 手机设备上的路径

2.7 Get the element structure in the current screen

driver.page_source  
作用:
    返回当前页面的文档结构,判断特定的元素是否存在

3. Mobile phone control viewing tool uiautomatorviewer

3.1 Tool Introduction

用来扫描和分析Android应用程序的UI控件的工具.

3.2 Operation steps

1.进入SDK目录下的tools目录,打开uiautomatorviewer

2.电脑连接真机或打开android模拟器

3.启动待测试app

4.点击uiautomatorviewer的左上角Device Screenshot,会生成app当前页面的UI控件截图

 

5.选择截图上需要查看的控件,即可浏览该控件的id,class,text,坐标等信息

4. APP element positioning operation

4.1 Commonly used element positioning methods in Appium

 4.2 Preamble code

    from appium import webdriver
    # server 启动参数
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    # app的信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'

    # 声明我们的driver对象
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

 4.3 Positioning by id

  方法:find_element_by_id(id_value) # id_value:为元素的id属性值
  业务场景:
      1.进入设置页面
      2.通过ID定位方式点击搜索按钮
  代码实现:
      driver.find_element_by_id("com.android.settings:id/search").click()
      driver.quit()

4.4 Positioning by class

  方法:find_element_by_class_name(class_value) # class_value:为元素的class属性值
    业务场景:
        1.进入设置页面
        2.点击搜索按钮
        3.通过class定位方式点击输入框的返回按钮
    代码实现:
        # id 点击搜索按钮
        driver.find_element_by_id("com.android.settings:id/search").click()
        # class 点击输入框返回按钮
        driver.find_element_by_class_name('android.widget.ImageButton').click()
        driver.quit()

4.5 Location via xpath

  方法:find_element_by_xpath(xpath_value) # xpath_value:为可以定位到元素的xpath语句
  *** android端xptah常用属性定位:
      1. id ://*[contains(@resource-id,'com.android.settings:id/search')] 
      2. class ://*[contains(@class,'android.widget.ImageButton')]
      3. text ://*[contains(@text,'WLA')]

  *** 模糊定位 contains(@key,value): value可以是部分值
    业务场景:
        1.进入设置页面
        2.点击WLAN菜单栏
    代码实现:
        # xpath 点击WLAN按钮
        driver.find_element_by_xpath("//*[contains(@text,'WLA')]").click()

4.6 Show waiting

    在一个超时时间范围内,每隔一段时间去搜索一次元素是否存在,
    如果存在返回定位对象,如果不存在直到超时时间到达,报超时异常错误。
    方法:WebDriverWait(driver, timeout, poll_frequency).until(method)
    参数:
        1.driver:手机驱动对象
        2.timeout:搜索超时时间
        3.poll_frequency:每次搜索间隔时间,默认时间为0.5s
        4.method:定位方法(匿名函数)
    匿名函数: 
        lambda x: x
    等价于python函数:
        def test(x):
            return x
    使用示例:
        WebDriverWait(driver, timeout, poll_frequency).until(lambda x: x.find_elements_by_id(id_value))
    解释:
        1.x传入值为:driver,所以才可以使用定位方法.
    函数运行过程:
        1.实例化WebDriverWait类,传入driver对象,之后driver对象被赋值给WebDriverWait的一个类变量:self._driver
        2.until为WebDriverWait类的方法,until传入method方法(即匿名函数),之后method方法会被传入self._driver
        3.搜索到元素后until返回定位对象,没有搜索到函数until返回超时异常错误.
    业务场景:
        1.进入设置页面
        2.通过ID定位方式点击搜索按钮
    代码实现:
        from selenium.webdriver.support.wait import WebDriverWait # 导入WebDriverWait类
        # 超时时间为30s,每隔1秒搜索一次元素是否存在,如果元素存在返回定位对象并退出
        search_button = WebDriverWait(driver, 30, 1).until(lambda driver: driver.find_element_by_id("com.android.settings:id/search"))
        search_button.click()
        driver.quit()

5. APP element information operation API

Preamble code:

    from appium import webdriver
    # server 启动参数
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    # app的信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'

    # 声明我们的driver对象
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

5.1 Send data to the input box

  方法:send_keys(vaue) # value:需要发送到输入框内的文本
  业务场景:
      1.打开设置
      2.点击搜索按钮
      3.输入内容abc
    代码实现:
        # 点击搜索按钮
        driver.find_element_by_id("com.android.settings:id/search").click()
        # 定位到输入框并输入abc
        driver.find_element_by_id("android:id/search_src_text").send_keys("abc")

    重点:
        大家可以将输入的abc 改成 输入中文,得到的结果:输入框无任何值输入且程序不会抱错
解决输入中文问题:

1.server 启动参数增加两个参数配置
      desired_caps['unicodeKeyboard'] = True
      desired_caps['resetKeyboard'] = True

2.再次运行会发现运行成功
       # 点击搜索按钮
       driver.find_element_by_id("com.android.settings:id/search").click()
       # 定位到输入框并输入abc
       driver.find_element_by_id("android:id/search_src_text").send_keys("传智播客")

 5.2 Clear the content of the input box

  方法:clear()
  业务场景:
      1.打开设置
      2.点击搜索按钮
      3.输入内容abc
      4.删除已输入abc
  代码实现:
      # 点击搜索按钮
      driver.find_element_by_id("com.android.settings:id/search").click()
      # 定位到输入框并输入abc
      input_text = driver.find_element_by_id("android:id/search_src_text")
      # 输入abc
      input_text.send_keys("abc")
      time.sleep(1)
      # 删除abc
      input_text.clear()

5.3 Get the text content of an element

  方法: text
  业务场景:
      1.进入设置
      2.获取所有元素class属性为“android.widget.TextView”的文本内容
  代码实现:
      text_vlaue = driver.find_elements_by_class_name("android.widget.TextView")
      for i in text_vlaue:
          print(i.text)
  执行结果:
      设置

      无线和网络
      WLAN
      更多
      设备
      显示
      提示音和通知
      存储

5.4 Get the attribute value of an element

  方法: get_attribute(value) # value:元素的属性
  ⚠️ value='name' 返回content-desc / text属性值
  ⚠️ value='text' 返回text的属性值
  ⚠️ value='className' 返回 class属性值,只有 API=>18 才能支持
  ⚠️ value='resourceId' 返回 resource-id属性值,只有 API=>18 才能支持
  业务场景:
      1.进入设置
      2.获取搜索按钮的content-desc属性值

 

  代码实现:
      # 定位到搜索按钮
      get_value = driver.find_element_by_id("com.android.settings:id/search")
      print(get_value.get_attribute("content-desc"))
  执行结果:
      搜索

5.5 Get the coordinates of the element on the screen

  方法:location
  业务场景:
      1.进入设置页面
      2.获取搜索按钮在屏幕的坐标位置
  代码实现:
      # 定位到搜索按钮
      get_value = driver.find_element_by_id("com.android.settings:id/search")
      # 打印搜索按钮在屏幕上的坐标
      print(get_value.location)
  执行结果:
      {'y': 44, 'x': 408}

5.6. Get app package name and startup name

  获取包名方法:current_package
  获取启动名:current_activity
  业务场景:
      1.启动设置
      2.获取包名和启动名
  代码实现:
      print(driver.current_package)
      print(driver.current_activity)
  执行结果:
      com.tencent.news
      .activity.SplashActivity

6. APP element event operation API

Preamble code:

    from appium import webdriver
    # server 启动参数
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    # app的信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'

    # 声明我们的driver对象
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

6.1 swip event

  ⚠️从一个坐标位置滑动到另一个坐标位置,只能是两个点之间的滑动
  方法:swipe(start_x, start_y, end_x, end_y, duration=None)
  参数:
      1.start_x:起点X轴坐标
      2.start_y:起点Y轴坐标
      3.end_x:  终点X轴坐标
      4.end_y,: 终点Y轴坐标
      5.duration: 滑动这个操作一共持续的时间长度,单位:ms
  业务场景:
      1.进入设置
      2.从坐标(148,659)滑动到坐标(148,248)
  代码实现:
      # 滑动没有持续时间
      driver.swipe(188,659,148,248)
      # 滑动持续5秒的时间
      driver.swipe(188,659,148,248,5000)

6.2 scroll event

  ⚠️ 从一个元素滑动到另一个元素,直到页面自动停止
  方法:scroll(origin_el, destination_el)
  参数:
      1.origin_el:滑动开始的元素
      2.destination_el:滑动结束的元素
  业务场景:
      1.进入设置页
      2.模拟手指从存储菜单位置 到 WLAN菜单位置的上滑操作
  代码实现:
      # 定位到存储菜单栏
      el1 = driver.find_element_by_xpath("//*[contains(@text,'存储')]")
      # 定位到WLAN菜单栏
      el2 = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
      # 执行滑动操作
      driver.scroll(el1,el2)

6.3 drag event

  ⚠️ 从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置
  方法:drag_and_drop(origin_el, destination_el)
  参数:
      1.origin_el:滑动开始的元素
      2.destination_el:滑动结束的元素
  业务场景:
      1.进入设置页
      2.模拟手指将存储菜单 滑动到 WLAN菜单栏位置
  代码实现:
      # 定位到存储菜单栏
      el1 = driver.find_element_by_xpath("//*[contains(@text,'存储')]")
      # 定位到WLAN菜单栏
      el2 = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
      # 执行滑动操作
      driver.drag_and_drop(el1,el2)

6.4 Application placed in the background event

  APP放置后台,模拟热启动
  方法:background_app(seconds)
  参数:
      1.seconds:停留在后台的时间,单位:秒
  业务场景:
      1.进入设置页
      2.将APP置于后台5s
  代码实现:
      driver.background_app(5)
  效果:
      app置于后台5s后,再次展示当前页面

Seven. APP simulation gesture advanced operation

TouchAction是AppiumDriver的辅助类,主要针对手势操作,比如滑动、长按、拖动等,
原理是将一系列的动作放在一个链条中发送到服务器,服务器接受到该链条后,解析各个动作,逐个执行。

Preamble code:

    from appium import webdriver
    # server 启动参数
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    # app的信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'

    # 声明我们的driver对象
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

7.1 Finger tap operation

  模拟手指轻敲一下屏幕操作
  方法:tap(element=None, x=None, y=None)
  方法:perform() # 发送命令到服务器执行操作
  参数:
      1.element:被定位到的元素
      2.x:相对于元素左上角的坐标,通常会使用元素的X轴坐标
      3.y:通常会使用元素的Y轴坐标
  业务场景:
      1.进入设置
      2.点击WLAN选项
  代码实现:
      # 通过元素定位方式敲击屏幕
      el = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
      TouchAction(driver).tap(el).perform()

      # 通过坐标方式敲击屏幕,WLAN坐标:x=155,y=250
      # TouchAction(driver).tap(x=155,y=250).perform()

7.2 Finger press operation

 模拟手指按下屏幕,按就要对应着离开.
  方法:press(el=None, x=None, y=None)
  方法:release() # 结束动作,手指离开屏幕
  参数:
      1.element:被定位到的元素
      2.x:通常会使用元素的X轴坐标
      3.y:通常会使用元素的Y轴坐标
  业务场景:
      1.进入设置
      2.点击WLAN选项
  代码实现:
      # 通过元素定位方式按下屏幕
      el = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
      TouchAction(driver).press(el).release().perform()

      # 通过坐标方式按下屏幕,WLAN坐标:x=155,y=250
      # TouchAction(driver).press(x=155,y=250).release().perform()

7.3 Waiting for operation

  方法:wait(ms=0)
  参数:
      ms:暂停的毫秒数
  业务场景:
      1.进入设置
      2.点击WLAN选项
      3.长按WiredSSID选项5秒
  代码实现:
      # 点击WLAN
      driver.find_element_by_xpath("//*[contains(@text,'WLAN')]").click()
      # 定位到WiredSSID
      el =driver.find_element_by_id("android:id/title")
      # 通过元素定位方式长按元素
      TouchAction(driver).press(el).wait(5000).perform()

      # 通过坐标方式模拟长按元素
      # 添加等待(有长按)/不添加等待(无长按效果)
      # TouchAction(driver).press(x=770,y=667).wait(5000).release().perform()

7.4 Finger long press operation

模拟手机按下屏幕一段时间,按就要对应着离开.
  方法:long_press(el=None, x=None, y=None, duration=1000)
  参数:
      1.element:被定位到的元素
      2.x:通常会使用元素的X轴坐标
      3.y:通常会使用元素的Y轴坐标
      4.duration:持续时间,默认为1000ms
  业务场景:
      1.进入设置
      2.点击WLAN选项
      3.长按WiredSSID选项5秒
  代码实现:
      # 点击WLAN
      driver.find_element_by_xpath("//*[contains(@text,'WLAN')]").click()
      # 定位到WiredSSID
      el =driver.find_element_by_id("android:id/title")
      # 通过元素定位方式长按元素
      TouchAction(driver).long_press(el,duration=5000).release().perform()

      # 通过坐标方式长按元素,WiredSSID坐标:x=770,y=667
      # 添加等待(有长按)/不添加等待(无长按效果)
      # TouchAction(driver).long_press(x=770,y=667).perform()

7.5 Finger movement operation

  模拟手机的滑动操作
  方法:move_to(el=None, x=None, y=None)
  参数:
      1.el:定位的元素
      2.x:相对于前一个元素的X轴偏移量
      3.y:相对于前一个元素的Y轴偏移量
  业务场景1:
      1.进入设置
      2.向上滑动屏幕
    代码实现:
        # 定位到存储
        el = driver.find_element_by_xpath("//*[contains(@text,'存储')]")
        # 定位到更多
        el1 = driver.find_element_by_xpath("//*[contains(@text,'更多')]")
        # 元素方式滑动
        TouchAction(driver).press(el).move_to(el1).release().perform()
        # 坐标的方式滑动
        # TouchAction(driver).press(x=240,y=600).wait(100).move_to(x=240,y=100).release().perform()
        # 注意press连接一个move_to实际调用的是swip方法,可在log中查询,不要给相对坐标。
    业务场景2:
        1.进入设置
        2.向上滑动屏幕到可见"安全"选项
        3.进入到安全
        4.点击屏幕锁定方式
        5.点击图案
        6.绘制图案
    代码实现:
        # 定位到WLAN
        el1 = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
        # 定位到存储
        el2 = driver.find_element_by_xpath("//*[contains(@text,'存储')]")
        # 存储上滑到WLAN
        driver.drag_and_drop(el2,el1)
        # 定位到用户
        el3 = driver.find_element_by_xpath("//*[contains(@text,'用户')]")
        # 注意 这次使用drag_and_drop方法,传入的"存储定位"仍使用其原始在屏幕上的位置,所以是由存储滑动到用户才可以上滑,否则需要重新"定位存储"
        # 存储上滑倒用户位置
        driver.drag_and_drop(el2,el3)
        # 点击安全按钮
        driver.find_element_by_xpath("//*[contains(@text,'安全')]").click()
        # 点击屏幕锁定方式按钮
        driver.find_element_by_xpath("//*[contains(@text,'屏幕锁定')]").click()
        # 点击图案按钮
        driver.find_element_by_xpath("//*[contains(@text,'图案')]").click()
        # 绘制图案四个坐标 1:(244,967) 2:(723,967) 3:(723,1442) 4:(244,1916)
        TouchAction(driver).press(x=244,y=967).wait(100).move_to(x=479,y=0).wait(100)\
            .move_to(x=0,y=475).wait(100).move_to(x=-479,y=474).release().perform()

Guess you like

Origin blog.csdn.net/qq_44954371/article/details/126708945