概述
1、本人进入IT行业第一份工作是测试自动化方向的,所以针对这一段工作,对自动化测试工具Appium的学习、使用作出整理和总结。
2、对于appium工具的基础使用,注意以下几个关键字:
(1)appium只是针对android、ios等的自动化工具进行了集成;还有就是appium这个集成工具是使用nodejs开发的。
(2)Appium clients list。
(3)Appium Server Parameters。
(4)Capabilities Doc。
(4)appium/appium-idb。
(5)对于更深入的原理学习可以在github网站上查找,例如:
Conceptual Introduction。
针对python3语言对appium工具基础使用的笔记
如果系统上成功安装了Appium工具,则按如下方式使用Appium Python Client:
(1)从PyPi安装’Appium-Python-Client’模块:
pip install Appium-Python-Client
(2)可以开始编写脚本使用Appium-Python-Client:
(a)Android environment(真机):
from appium import webdriver
desired_caps = {}
desired_caps['platformName'] = 'Android'
# desired_caps['platformVersion'] = '8.1'
# desired_caps['automationName'] = 'uiautomator2'
desired_caps['deviceName'] = 'Android'
# desired_caps['app'] = PATH('../../../apps/selendroid-test-app.apk')
desired_caps['appActivity'] = '...' # 可以使用adb工具获取
desired_caps['appPackage'] = '...' # 可以使用adb工具获取
desired_caps['udid'] = '...' # 可以使用 $ adb devices 获取
desired_caps['ignoreUnimportantViews'] = 'true'
# ...
# 如果需要其他的参数可以查看`Capabilities Doc`,并添加
# ...
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# ...
(b)iOS environment(真机):
from appium import webdriver
desired_caps = {}
desired_caps['platformName'] = 'iOS'
# desired_caps['platformVersion'] = '11.4'
desired_caps['automationName'] = 'XCUITest'
desired_caps['deviceName'] = 'iPhone'
# desired_caps['app'] = PATH('../../apps/UICatalog.app.zip')
desired_caps['udid'] = '...' # 可以使用 $ idevice_id -l 获取
desired_caps['bundleId'] = '...' # 可以使用 $ ideviceinstaller -l 获取
# ...
# 如果需要其他的参数可以查看`Capabilities Doc`,并添加
# ...
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# ...
针对IOS自动化的相关笔记
1、在Mac OS系统下完成安装Appium。
2、针对WDA(WebDriverAgent)的使用笔记:
以下python3代码在Mac OS 10.13.6、Xcode10.0、Appium1.8.0、IOS12.0.1、Python 3.6.3工具环境下,并在第一步中成功安装Appium的条件下,测试成功。
代码的目的是为了实现IOS移动设备的切网功能,下面代码实现了核心代码,没有实现切网的全部代码。
(1)前面提到了Appium工具是一个集成工具,针对IOS的自动化实现,是使用了WDA工具;
(2)使用WDA可以实现对手机的直接控制,具体介绍和简单使用如下:
a. 先使用xcodebuild命令启动WDA软件;
project = "/Applications/Appium.app/Contents/Resources/app/node_modules\
/appium/node_modules/appium-xcuitest-driver\
/WebDriverAgent/WebDriverAgent.xcodeproj"
scheme = 'WebDriverAgentRunner'
commond = "xcodebuild -project %s -scheme %s -destination 'id=%s' test" % (project, scheme, udid)
b. 再使用iproxy命令,使得Mac电脑指定端口与IOS设备上WDA软件的端口进行映射;
commond = "iproxy %s 8100 %s" % (mac_port, udid)
c. 在a、b步骤中的命令工具都成功启动后,即可使用HTTP请求的方式访问Mac本地(即localhost)的指定的不同端口(即mac_port)来控制对应的IOS设备。
例如,使用GET方式,访问“http://localhost:
def get_session(mac_port):
"""访问Mac上的指定端口,获取指定手机与Mac的SessionId"""
rs = http_get2('http://localhost:%s/status' % mac_port)
if not rs.startswith('error'):
rs_json = json.loads(rs, encoding='utf-8')
session_id = rs_json['sessionId']
return session_id
上面的三个步骤简单阐述了直接使用WDA控制IOS移动设备的方法。更多信息可参考Github网站相关资源
完整测试代码如下:
#!/usr/bin/env python3
import threading
import subprocess
import json
import urllib.request
import time
from xml.dom import minidom
from http import client
def ex_cmd3(cmd):
"""子进程工具:
xcodebuild & iproxy命令的子进程没有进行时间控制,而是采用任务完成后直接杀进程。
该处可以自行优化。"""
p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
p.wait()
def http_get2(url):
"""发送GET请求"""
try:
gt = urllib.request.urlopen(url,timeout=60)
except Exception as ex:
res = 'error:%s' % ex
else:
if gt.getcode() == 200:
resp = gt.read()
resp_str = resp.decode('utf8')
res = resp_str
else:
res = 'error:%d' % gt.getcode()
gt.close()
return res
def req_post(req_data, session_id, action, mac_port):
"""发送POST请求,进行操作ios界面内容。"""
req_data_json = json.dumps(req_data)
url = 'http://localhost:%s/session/' % (mac_port) + session_id + action
header = {"Content-type": "application/json"}
conn = client.HTTPConnection("localhost", int(mac_port))
conn.request('POST', url, req_data_json, header)
res = conn.getresponse()
res = res.read()
conn.close()
return res
def start_up_wda(mac_port,udid):
""" 启动WebDriverAgent工具,并获取指定IOS移动设备的session id。 """
def startup_wda(mac_port, udid):
"""启动xcodebuild & iproxy:
WDA为IOS软件包,使用xcodebuild命令启动;
使用iproxy命令使Mac与iPhone建立端口映射。"""
project = "/Applications/Appium.app/Contents/Resources/app/node_modules\
/appium/node_modules/appium-xcuitest-driver\
/WebDriverAgent/WebDriverAgent.xcodeproj"
# print(project)
scheme = 'WebDriverAgentRunner'
commond = "xcodebuild -project %s -scheme %s -destination 'id=%s' test" % (project, scheme, udid)
threading.Thread(target=ex_cmd3, args=(commond,)).start() # xcodebuild命令的更多信息,使用 $ xcodebuild --help来查询
print("xcodebuild命令行已启动!")
time.sleep(30)
commond = "iproxy %s 8100 %s" % (mac_port, udid) # 每个IOS上WDA软件的服务端口为8100 # 而Mac由于连接多个手机,Mac上的端口可以自行设定。
threading.Thread(target=ex_cmd3, args=(commond,)).start()
print("iproxy命令行已启动!")
def get_session(mac_port):
""" 访问Mac上的指定端口,获取指定IOS移动设备与Mac电脑的 session id。 """
rs = http_get2('http://localhost:%s/status' % mac_port)
if not rs.startswith('error'):
rs_json = json.loads(rs, encoding='utf-8')
session_id = rs_json['sessionId']
return session_id
startup_wda(mac_port, udid)
time.sleep(20)
session_id = get_session(mac_port)
return session_id
def get_source(mac_port, session_id):
""" 获取source """
rs = http_get2('http://localhost:%s/session/%s/source' % (mac_port, session_id))
source = ''
if not rs.startswith('error'):
rs_json = json.loads(rs, encoding='utf-8')
source = rs_json['value']
return source
def write_source_xml(str_source, session_id):
""" 将获取到的source写入xml文件 """
source_xml = open(session_id + '_source.xml', 'w')
source_xml.write(str_source)
source_xml.close()
print(session_id + '_source.xml文件创建成功!')
def get_target(element_type, session_id, element_name):
""" 从pagesource中获取目标控件的中间点。 """
with open(session_id + '_source.xml', 'r', encoding='utf8') as fh:
dom = minidom.parse(fh)
root = dom.documentElement
# XCUIElementTypeIcon XCUIElementTypeCell
iconList = root.getElementsByTagName(element_type)
for icon in iconList:
name = icon.getAttribute('name')
if element_name == name:
x = int(icon.getAttribute('x'))
y = int(icon.getAttribute('y'))
width = int(icon.getAttribute('width'))
height = int(icon.getAttribute('height'))
break
return {"x": x+width/2,"y": y+height/2}
def tap_by_name(session_id, mac_port, name):
""" 使用POST请求,访问http://localhost:/session/wda/tap/0,进行点击目标控件。 """
source = get_source(mac_port, session_id)
if source != '':
write_source_xml(source, session_id)
if 'set' == name:
req_data = get_target('XCUIElementTypeIcon', session_id, "设置")
req_post(req_data, session_id, '/wda/tap/0', mac_port)
source = "SUCCESS"
elif 'Wi-Fi' == name:
req_data = get_target('XCUIElementTypeCell', session_id, "Wi-Fi")
req_post(req_data, session_id, '/wda/tap/0', mac_port)
source = "SUCCESS"
else:
req_data = get_target('XCUIElementTypeCell', session_id, "无线局域网")
req_post(req_data, session_id, '/wda/tap/0', mac_port)
source = "SUCCESS"
return source
if __name__ == "__main__":
mac_port = "8723"
session_id = start_up_wda(mac_port, 'c259a97742e575999c9706a8b97cff7bf5046e9f')
source = tap_by_name(session_id, mac_port, 'set')
if source.startswith("SUCCESS"):
print("'set'点击成功!")
source = tap_by_name(session_id, mac_port, 'Wi-Fi')
if source.startswith("SUCCESS"):
print("'Wi-Fi'点击成功!")
# 杀掉进程可以使用 $ kill -9 `lsof -t -i:port` 命令;该部分代码可以自行实现,或使用更好的关闭子进程方式。