移动端自动化测试框架Appium

启动过程

appium的启动实际上是在本机使用了4723端口开启了一个服务

1. 我们写的 python 代码会访问本机的 appium 服务器,并获取 driver 对象

2. appium 会将我们的 driver 对象调用的方法转化成 post 请求,提交给appium服务器

3. appium 通过接收到的 post 请求发送给手机,再由手机进行执行

# 导模块 
from appium import webdriver 
# 创建一个字典,包装相应的启动参数 
desired_caps = dict() 
# 需要连接的手机的平台(不限制大小写) 
desired_caps['platformName'] = 'Android' 
# 需要连接的手机的版本号(比如 5.2.1 的版本可以填写 5.2.1 或 5.2 或 5 ,以此类推) desired_caps['platformVersion'] = '5.1' 
# 需要连接的手机的设备号(andoird平台下,可以随便写,但是不能不写) 
desired_caps['deviceName'] = '192.168.56.101:5555' 
# 需要启动的程序的包名 
desired_caps['appPackage'] = 'com.android.settings' 
# 需要启动的程序的界面名 
desired_caps['appActivity'] = '.Settings' 
# 连接appium服务器 
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps) 
# 退出 
driver.quit()

App基础操作API

import os

from app.util import encode_data, decode_data, get_driver

driver = get_driver()

"3、app是否已安装"
is_installed = driver.is_app_installed('com.example.corel.calc')
# eg:没安装安装,安装了就卸载
if is_installed:
    "2、卸载手机自己安装的app,不能卸载系统的app"
    driver.remove_app('com.example.corel.calc')
else:
    "1、安装apk到手机,支持重复安装进行覆盖,绝对路径"
    driver.install_app(os.getcwd() + os.sep + 'apk' + os.sep + 'com.example.corel.calc_2.1.1023_11.apk')


"""4、发送⽂件到⼿机,eg:将hello写入到手机
    driver.push_file(path, data)
        参数:
            path:⼿机设备上的路径(例如:/sdcard/a.txt)
            data:⽂件内数据,要求base64编码
            Python3.x中字符都为unicode编码,⽽b64encode函数的参数为byte类型,需要先转码;⽣成的数据为byte类型,需要将byte转换回去。"""

data = encode_data('hello')
driver.push_file('/sdcard/abc.txt', data)  # 不指定abc.txt,会生成一个.tmp结尾的临时文件,不便于后续的维护

"5、从手机拉取文件,返回的是base64编码数据"
data = driver.pull_file('/sdcard/abc.txt')   # 返回数据为base64编码
re_data = decode_data(data)
print(re_data)

"6、获取 当前屏幕 元素结构,返回xml字符串,只是当前屏幕(activity页面内容是xml承载的)"
with open('./result/page.xml', 'w') as f:
    f.write(driver.page_source)

driver.quit()
import base64

from appium import webdriver


# 输⼊⽂本内容到⼿机,中文输入支持:
#   1 使用Unicode键盘:'unicodeKeyboard': True
#   2 重置键盘:'resetKeyboard': True

def get_driver():
    desired_caps = {'platformName': 'android',
                    'platformVersionName': '5.1',
                    'deviceName': '192.168.192.101:5555',
                    'appPackage': 'com.android.settings',
                    'appActivity': '.Settings',
                    'unicodeKeyboard': True}
    return webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)


# base64编码
def encode_data(value_arg):
    return str(base64.b64encode(value_arg.encode('utf-8')), 'utf-8')


# base64解码
def decode_data(data):
    return str(base64.b64decode(data), 'utf-8')

APP元素定位操作

appium常用元素定位方式

name

value

id

resource-id属性值

class

class属性值

xpath

xpath表达式

from time import sleep
from selenium.webdriver.support.wait import WebDriverWait

#   ❀ 借助sdk家⽬录下的tools⽬录中的uiautomatorviewer.bat进行定位,而该shell脚本和appium共用同一个adb,
# 如果自动化脚本中因异常没有关闭adb服务,那此时进行快照截图的话是会报错的,解决办法:adb kill-server
# 三种定位使用优先级:
#     优先使用:id class
#     其次使用:xpath,需借助text、class、resource-id才可完成

# xpath语句:
#  1."//*[contains(@text,'text属性值')]"
#  2."//*[contains(@class,'class属性值')]"
#  3."//*[contains(@resource-id,'resource-id属性值')]"
from app.util import get_driver

driver = get_driver()
# 1、id获取搜索按钮并点击
driver.find_element_by_id('com.android.settings:id/search').click()

# 3.1 xpath + id组合 获取搜索按钮并点击
search_btn = '//*[contains(@resource-id, "com.android.settings:id/search")]'
driver.find_element_by_xpath(search_btn).click()
sleep(2)

# 2、class获取返回按钮并点击
driver.find_element_by_class_name('android.widget.ImageButton').click()

# 3.2 xpath + class组合 获取返回按钮并点击
search_return_btn = '//*[contains(@class, "android.widget.ImageButton")]'
driver.find_element_by_xpath(search_return_btn).click()
sleep(2)

# 3.3 xpath + text 获取返回按钮并点击
more_btn = '//*[contains(@text, "更")]'
driver.find_element_by_xpath(more_btn).click()
sleep(2)

# 定位一组元素id,class,xpath
ids_value = driver.find_elements_by_id('com.android.settings:id/title')
for i in ids_value:
    print(i.text)

print('❀______')

classes_value = driver.find_elements_by_class_name('android.widget.TextView')
for i in classes_value:
    print(i.text)

print('❀______')

xpath_value = '//*[contains(@text, "示")]'
xpaths_value = driver.find_elements_by_xpath(xpath_value)
for i in xpaths_value:
    print(i.text)

# 隐式等待:是等到整个页面加载结束才进行后续操作
# 显示等待:是等待某一个具体的元素成功获取
driver.find_element_by_xpath('//*[contains(@text, "更多")]').click()
WebDriverWait(driver, 10).until(lambda x: x.find_element_by_xpath('//*[contains(@text, "飞行模式")]')).click()

driver.quit()

APP元素信息操作API

from app.util import get_driver

driver = get_driver()

search = driver.find_element_by_id('com.android.settings:id/search')
search.click()
search_input = driver.find_element_by_id('android:id/search_src_text')
search_input.send_keys('文')
search_input.clear()
search_input.send_keys('星魂')

# 获取元素id class text content-desc的属性值:
# get_attribute(value):
#     value = name -> 返回content-desc或text
#     value = text -> 返回text
#     value = className -> 返回class,只有API>=18(Android4.3)才能支持
#     value = resourceId -> 返回resource_id,只有API>=18才能支持

print('resource_id:', search.get_attribute('resourceId'))
print('class:', search.get_attribute('className'))
print('content_desc:', search.get_attribute('name'))

# 应用场景:只是想滑动轮播图,并不想整个页面滑动
more_btn = '//*[contains(@text, "更")]'
more = driver.find_element_by_xpath(more_btn)
# 1 获取元素屏幕座标
print('座标:' + more.location)
# 2 获取元素大小
print('宽和高:' + more.size)


# 获取包命和启动名
print('包命:', driver.current_package)
print('启动名:', driver.current_activity)

driver.quit()

APP元素事件操作API

swipe滑动事件

import time

from util import get_driver

# swipe(start_x, start_y, end_x, end_y, duration=None),duration单位ms
# 作用:
#   1 滑动没有持续时间,滑动相对随机,随机距离不远
#   2 滑动持续在2s左右时,滑动相对准确  --最常用
#   3 滑动持续为几十毫秒时,会产生点击起点位置操作

driver = get_driver()

save = driver.find_element_by_xpath("//*[contains(@text,'存储')]").location
wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]").location

# driver.swipe(save.get("x"), save.get("y"), wlan.get("x"), wlan.get("y"))

# 滑动,有滑动时间,滑动相对准确 最少2s
driver.swipe(save.get("x"), save.get("y"), wlan.get("x"), wlan.get("y"), 2000)

# 滑动,有滑动时间,滑动相对较长 最少200ms  但如果时几十毫秒,会产生点击起点位置操作
driver.swipe(save.get("x"), save.get("y"), wlan.get("x"), wlan.get("y"), 200)

# 关闭
time.sleep(2)
driver.quit()

scroll滑动事件

import time

from util import get_driver

# scroll(origin_el, destination_el, duration=None)
# 滑动相对随机,不用

driver = get_driver()

save = driver.find_element_by_xpath("//*[contains(@text,'存储')]").location
wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]").location

# 无滑动时间
driver.scroll(save, wlan)
# 有滑动时间2s
driver.scroll(save, wlan, 2000)

# 关闭
time.sleep(2)
driver.quit()

drag拖拽事件

import time

from util import get_driver

# drag_and_drop(origin_el, destination_el):从⼀个元素滑动到另⼀个元素,起点元素最终会替代终点元素原本屏幕上的位置

driver = get_driver()

save = driver.find_element_by_xpath("//*[contains(@text,'存储')]").location
wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]").location

driver.drag_and_drop(save, wlan)

# 关闭
time.sleep(2)
driver.quit()
from time import sleep

from appium.webdriver.common.touch_action import TouchAction

from util import get_driver

driver = get_driver()

save = driver.find_element_by_xpath("//*[contains(@text,'存储')]")
more = driver.find_element_by_xpath("//*[contains(@text,'更多')]")

touch_action = TouchAction(driver)

# 滑动方式一:press(元素).move_to(元素)  滑动不精准,scroll底层实现方式
touch_action.press(save).move_to(more).release().perform()
# 滑动方式二:press(座标).move_to(座标)  滑动不精准,swipe底层实现方式
touch_action.press(x=save.location.get('x'), y=save.location.get('y')).move_to(x=more.location.get('x'),
                                                                               y=more.location.get(
                                                                                   'y')).release().perform()
# 滑动方式三:long_press(元素).move_to(座标)  精准滑动,drag_and_drop底层实现方式
touch_action.long_press(save).move_to(more).release().perform()

sleep(2)
driver.quit()

 

应⽤置于后台事件

from time import sleep

from util import get_driver

driver = get_driver()
sleep(1)

# 将设置app放置后台5s,在此期间,点击桌面应用无效,因为它并没有运行结束,而是在后台运行
driver.background_app(5)

driver.quit()

APP模拟⼿势⾼级操作

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

from time import sleep

from appium.webdriver.common.touch_action import TouchAction

from util import get_driver

driver = get_driver()

wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")
touch_action = TouchAction(driver)

"""1 手指轻敲操作:模拟⼿指轻敲⼀下屏幕操作"""
# TouchAction(driver).tap(wlan).perform()  # 元素方式
touch_action.tap(x=wlan.location.get('x'), y=wlan.location.get('y')).perform() # 座标方式

"""2 ⼿指按操作:: 模拟⼿指按下屏幕,然后松开"""
touch_action.press(wlan).release().perform()  # 元素方式
touch_action.press(x=wlan.location.get('x'), y=wlan.location.get('y')).perform() # 座标方式

"""3 等待操作:wait(ms=0)"""
# 方式一:press + wait
wlan.click()
ssid = driver.find_element_by_xpath("//*[contains(@text,'SSID')]")
# 添加等待(有⻓按)/不添加等待(⽆⻓按效果)
touch_action.press(ssid).wait(2000).release().perform()

# 方式二:long_press
touch_action.long_press(ssid).release().perform()  # 元素方式
touch_action.long_press(x=ssid.location.get('x'), y=wlan.location.get('y')).release().perform()  # 座标方式


sleep(2)
driver.quit()
from time import sleep

from appium.webdriver.common.touch_action import TouchAction

from util import get_driver

# appium版本 1.12
#   move_to:绝对座标
# appium版本 1.7之前
#   move_to:相对座标

driver = get_driver()
bat = driver.find_element_by_xpath("//*[contains(@text,'电池')]")
wlan = driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")

driver.drag_and_drop(bat, wlan)
driver.find_element_by_xpath("//*[contains(@text,'安全')]").click()
sleep(2)
driver.find_element_by_xpath("//*[contains(@text,'屏幕锁定')]").click()
sleep(2)
driver.find_element_by_xpath("//*[contains(@text,'图案')]").click()

sleep(2)

TouchAction(driver).press(x=234, y=850).wait(100).move_to(x=723, y=850).wait(100) \
    .move_to(x=723, y=1326).wait(100).move_to(x=234, y=1808).release().perform()

sleep(2)
driver.quit()

⼿机操作API

from time import sleep

from util import get_driver

driver = get_driver()

# 1 获取手机分辨率,通常用于swipe方法完成滑动操作
size = driver.get_window_size()
print('分辨率:', size)

width = size.get('width')
height = size.get('height')

driver.swipe(width * 0.5, height * 0.8, width * 0.5, height * 0.2, 2000)

sleep(3)
driver.close_app()
driver.swipe(width * 0.8, height * 0.5, width * 0.2, height * 0.5, 2000)

sleep(3)
driver.quit()

 发送键到设备

from time import sleep

from util import get_driver

driver = get_driver()

# 模拟音量键 +
for i in range(3):
    driver.keyevent(24)
# 模拟音量键 -
for i in range(3):
    driver.keyevent(25)

sleep(3)
driver.quit()

 操作⼿机通知栏

from appium import webdriver
import time

# server 启动参数
from appium.webdriver.common.touch_action import TouchAction

desired_caps = {}
# 设备信息
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.56.101:5555'
# app的信息
desired_caps['appPackage'] = 'io.manong.developerdaily'
desired_caps['appActivity'] = 'io.toutiao.android.ui.activity.MainActivity'
# 声明我们的driver对象
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

# 打开通知栏
driver.open_notifications()
# 获取时间 id
value = driver.find_element_by_id("com.android.systemui:id/date_expanded").text
print("时间为:", value)

time.sleep(2)
driver.quit()

获取⼿机当前⽹络

from appium import webdriver
import time

# server 启动参数
from appium.webdriver.common.touch_action import TouchAction

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)

# 打印手机网络状态
print("当前网络为:", driver.network_connection)

# 修改手机网络 data
driver.set_network_connection(4)
# 打印手机网络状态
print("当前网络为:", driver.network_connection)

# 修改手机网络
driver.set_network_connection(2)
# 打印手机网络状态
print("当前网络为:", driver.network_connection)

# 修改手机网络
driver.set_network_connection(1)
# 打印手机网络状态
print("当前网络为:", driver.network_connection)






time.sleep(2)
driver.quit()

⼿机截图

from appium import webdriver
import time

# server 启动参数
from appium.webdriver.common.touch_action import TouchAction

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)

# 截图
driver.get_screenshot_as_file("{}.png".format(int(time.time())))   # 1583378495 不加int:1583378546.1369514

time.sleep(2)
driver.quit()

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章