Desired Capabilities-Appium 自动化配置项
设置参数:操作系统、版本、设备名称、包名(应用程序)、应用程序入口启动页面
简介
Desired capabilities 是一些键值对的集合。python里面就采用字典的方式。
客户端将这些键值对发给服务端,告诉服务端我们想要怎么测试。比如,我们可以把 platformName 的 capability 设置为Android,就是告诉 Appium 服务端,我们想要一个 Android 的 session,而不是一个 IOS 的。
我们在新建driver之前,会先定义好 desired_caps。
APP desired_caps 定义
比如下面的代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/8/14 13:43
# @Author : shisuiyi
# @File : first_app.py
# @Software: win10 Tensorflow1.13.1 python3.7
from appium import webdriver
# 1.设置终端参数
desired_caps = {
"platformName": "Android",
"automationName": "UiAutomator2",
"platformVersion": "7.1.2",
"deviceName": "HUAWEI",
"appPackage": "com.lemon.lemonban",
"appActivity": "com.lemon.lemonban.activity.MainActivity",
"noReset": "True"
}
# 2.发送指令给appium server
webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)
# 还需要做哪些前期的准备工作
# 1.appium server 进行启动
# 2.模拟器或真机必须能够被电脑识别
# adb进行连接再查看连接设备, 如果连接则可以
这是一个典型的测试Android APP的方式。
我们采用字典的方式来装键值对。这里定义了我们要运行测试用例的平台,也就是 Android。定义了平台版本,也就是Android 7.1.2。定义了APP的包名以及Activity名。
这里指定了Package 以及 Activity ,所以可以不用指定 app。
常用的adb获取apk的包名与类名操作
获取指定路径App的包名
aapt dunp badging <file_path.apk> | findstr package
C:\Users\12446>aapt dunp badging C:\Users\12446\Desktop\lemon.apk | findstr package
package: name='com.lemon.lemonban' versionCode='20181113' versionName='2.1.2' platformBuildVersionName='2.1.2'
获取当前App的包名,持续监控
adb shell am monitor
Monitoring activity manager... available commands:
(q)uit: finish monitoring
** Activity starting: com.lemon.lemonban
** Activity starting: com.lemon.lemonban
** Activity resuming: com.lemon.lemonban
查看设备安装包名[第三方应用]
adb shell pm list packages [-3]
package:io.appium.settings
package:com.xxzb.fenwoo
package:com.github.uiautomator.test
package:com.tal.kaoyan
package:io.appium.uiautomator2.server
package:com.ibox.calculators
package:io.appium.uiautomator2.server.test
package:com.lemon.lemonban
package:com.netease.cloudmusic
获取前台运行的App包名与类名
adb shell dumpsys activity | find "mFocusedActivity"
Android8.0以前:adb shell dumpsys activity | find "mResumedActivity"
Android8.0以后:
C:\Users\12446>adb shell dumpsys activity | find "mFocusedActivity"
mFocusedActivity: ActivityRecord{d86efe4 u0 com.lemon.lemonban/.activity.MainActivity t11}
查看当前运行的包名和Activity-更清晰
adb shell dumpsys window | findstr mCurrentFocus
C:\Users\12446>adb shell dumpsys window | findstr mCurrentFocus
mCurrentFocus=Window{7d94803 u0 com.lemon.lemonban/com.lemon.lemonban.activity.MainActivity}
通过脚本自动化获取apk的包名和对应启动activity
使用androguard来获取
pip install androguard
from appium import webdriver
from androguard.core.bytecodes.apk import APK
def get_apkname(apk):
a = APK(apk, False, "r")
return a.get_package()
def get_apk_lautc(apk):
a = APK(apk, False, "r")
return a.get_main_activity()
desired_caps = {
'platformName': 'Android',
'deviceName': '59e14df4',#adb deivces
'platformVersion': '10', #从设置中可以获取
'appPackage': get_apkname("/Users//Downloads/com.tencent.mobileqq.apk"),#包名
'appActivity': get_apk_lautc("/Users//Downloads/com.tencent.mobileqq.apk") ,# apk的launcherActivity
'skipServerInstallation':True
}
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
通过脚本自动化获取设备deviceName和platformVersion
from appium import webdriver
from androguard.core.bytecodes.apk import APK
import os
apk_path="/Users/lileilei/Downloads/com.tencent.mobileqq_8.5.0_1596.apk"
def get_devices():
cmd="adb devices"
reslut=os.popen(cmd).readlines()[1:]
for item in reslut:
if item!="\n":
return str(item).split("\t")[0]
def getPlatForm():
cmd='adb shell getprop ro.build.version.release'
reslut = os.popen(cmd).readlines()[0]
return str(reslut).split("\n")[0]
def get_apkname(apk):
a = APK(apk, False, "r")
return a.get_package()
def get_apk_lautc(apk):
a = APK(apk, False, "r")
return a.get_main_activity()
desired_caps = {
'platformName': 'Android',
'deviceName': get_devices(),
'platformVersion':getPlatForm(),
'appPackage': get_apkname(apk_path),#包名
'appActivity': get_apk_lautc(apk_path) ,# apk的launcherActivity
'skipServerInstallation':True
}
driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
Desired capabilities 键值对 详细描述
通用的Desired capabilities 键值对:
此参数支持多种驱动程序
键 | 描述 | 值 |
---|---|---|
| 自动化测试的引擎 |
|
| 使用的手机操作系统 |
|
| 手机操作系统的版本 | 例如 |
| 使用的手机或模拟器类型 |
|
| 本地绝对路径_或_远程 http URL 所指向的一个安装包( |
|
| 在运行测试之前要安装的应用程序或应用程序列表(作为JSON排列)。请注意, | 例如 |
| 做自动化时使用的浏览器名字。如果是一个应用则只需填写个空的字符串。 | ‘Safari’ 对应 iOS,‘Chrome’, ‘Chromium’, 或 ‘Browser’ 则对应 Android |
| 用于客户端在退出或者结束 session 之前,Appium 等待客户端发送一条新命令所花费的时间(秒为单位)。 | 例如 |
| iOS (仅XCUITest driver )和 Android 为模拟器设置语言。 | 例如 |
| iOS (仅XCUITest driver )和 Android 为模拟器设置所在区域,fr_CA | 例如 |
| 连接真机的唯一设备号 | 例如 |
| (仅支持模拟器) 模拟器当前的方向 |
|
| 直接转换到 Webview 上下文(context)。默认值为 |
|
| 在当前 session 下不会重置应用的状态。见此文 |
|
| 执行一个完整的重置. 见 此文 |
|
| 启用或禁用各种Appium-internal事件的时间报告 (例如 每个命令的开始和结束,等等)。 默认值为 | 例如 |
| (仅Web和webview) 启用Chromedriver(Android)或Safari(iOS)的性能日志 (默认值为 |
|
| 当查找操作失败时,打印当前页源文件。 默认值为 | 例如 |
| 在会话结束时删除所有生成的文件。 默认值为 |
|
Android 独有
这些参数只能在基于Android drivers上使用 (例如 UiAutomator2)。
键 | 描述 | 值 |
---|---|---|
| Activity 的名字是指从你的包中所要启动的 Android acticity。他通常需要再前面添加 |
|
| 运行的 Android 应用的包名。默认情况下,此参数是从软件包manifest中接收的。(@package属性值) |
|
| Activity name/names,逗号分隔,用于你要等待的Android Activity。 :默认情况下,此参数的值和 |
|
| 用于等待启动的 Android 应用的包。默认情况下,此功能的值与 |
|
| 用于等待 appWaitActivity 启动的超时时间(以毫秒为单位)(默认值为 |
|
| 用于等待模拟器或真机准备就绪的超时时间 |
|
| 允许安装测试包(mainfest中 |
|
| 用于执行测试的 instrumentation 类。 传送 |
|
| 一个自己实现的广播操作,用于将覆盖率转储到文件系统中。传送 |
|
| 用于等待设备在启动应用后准备就绪的超时时间。以秒为单位。 | 例如 |
| 用于等待在设备中安装 apk 所花费的时间(以毫秒为单位)。默认值为 | 例如 |
| 安装前将在其中推送apk的设备上的目录名。默认值为 | 例如 |
| 用来连接 ADB 服务器的端口(默认值为 |
|
|
| 例如 |
| 可选的远程ADB服务器主机 | 例如 192.168.0.101 |
| 开发工具的 socket 名称。只有在被测应用是一个使用 Chromium 内核的浏览器时才需要。socket 会被浏览器打开,然后 Chromedriver 把它作为开发者工具来进行连接。 | 例如 |
| 被启动 avd 的名字 | 例如 |
| 用于等待 avd 启动并连接 ADB 的超时时间(以毫秒为单位),默认值为 |
|
| 用于等待 avd 完成启动动画的超时时间(以毫秒为单位),默认值为 |
|
| 启动 avd 时使用的额外参数 | 例如 |
| 使用自定义的 keystore 给 apk 签名,默认值为 |
|
| 自定义 keystore 的路径, 默认路径为 ~/.android/debug.keystore | 例如 |
| 自定义 keystore 的密码 | 例如 |
| key 的别名 | 例如 |
| key 的密码 | 例如 |
| webdriver 可执行文件的绝对路径(如果 Chromium 内嵌一个自己提供的 webdriver,则应使用他去替换掉 Appium 自带的 chromedriver) |
|
| 当由Appium运行时,要传递给chromedriver二进制文件的参数数组。默认情况下,除了Appium内部使用的以外,不会添加CLI参数 (例如 | 例如 |
| 用于查找Chromedriver可执行文件的目录的绝对路径,用于自动发现兼容的Chromedriver。忽略 |
|
| 将Chromedriver版本映射到它所支持的最小Chrome的文件的绝对路径。忽略 |
|
| 当 | 例如 |
| 用于等待 Webview 上下文(context)激活的时间(以毫秒为单位)。默认值为 | 例如 |
| 数字端口启动Chromedriver。请注意,不建议使用此功能,因为在有多个webview的情况下,它会导致未定义的行为。默认情况下,Appium会找到一个空闲端口。 | 例如 |
| Appium用于与Chromedrivers通信的有效端口列表。这个功能支持多种webview场景。此参数的形式是一个数值端口数组,数组项本身可以是长度为2的数组,其中第一个元素是包含范围的开始,第二个元素是结束。默认情况下,Appium将使用任何空闲端口。 | 例如 |
| Appium是否应该增强它的页面webview检测,以保证任何webview上下文显示在上下文列表有活动的页面。 这可以防止在Chromedriver无法找到任何页面的情况下选择上下文时发生的错误。 默认值为 | 例如 |
| 为了支持 | 例如 |
| 自Appium1.18.0+起启用通过 |
|
| 在使用adb启动应用程序之前,不要停止被测应用程序的进程。 如果被测试的应用程序是由另一个锚定应用程序创建的,则将其设置为false,则在使用adb启动测试应用程序的过程中,锚定应用程序的过程仍然可以运行。换句话说,当 |
|
| 使用 Unicode 输入法。 默认值为 |
|
| 在设定了 |
|
| 跳过检查和对应用进行 debug 签名的步骤。仅适用于 UiAutomator。 默认值为 |
|
| 调用 uiautomator 的函数 |
|
| 禁用 android 监视器(watchers)。监视器用于见识应用程序的无响应状态(anr)和崩溃(crash),禁用会降低 Android 设备或模拟器的 CPU 使用率。该 capability 仅在使用 UiAutomator 时有效。默认设置为 |
|
| 允许对 ChromeDriver 传 chromeOptions 的参数。了解更多信息请查阅 chromeOptions |
|
| 当移除非 ChromeDriver webview时,终止掉 ChromeDriver 的 session。默认设置为 |
|
| 在 web 的上下文(context),使用原生(native)的方法去截图,而不是用过代理的 ChromeDriver。默认值为 |
|
| 在设备中截图被保存的目录名。默认值为 | 例如 |
| 让Appium自动确定您的应用需要哪些权限,并在安装时将其授予应用。默认设置为 |
|
| 模拟设置网络速度。指定最大的网络上传和下载速度。 默认值为 |
|
| 在开始会话之前,为模拟器切换gps位置提供程序。 默认情况下,仿真器将根据配置方式启用或不启用此选项。 |
|
| 设置此参数为 | 例如 |
| 用于等待adb命令执行的超时(毫秒)。默认值为 | 例如 |
| 设置地区 见此文 | 例如 |
| 跳过设备初始化,其中包括i.a.:设置app的安装运行或权限设置。当设备已经被用于自动化并且为下一个自动化做好了准备时,可以用来提高启动性能。 默认值为 |
|
| 为Chrome webview测试设置chromedriver标志 |
|
| 在会话创建期间跳过解锁。 默认值为 |
|
| 使用特定的锁定模式解锁目标设备,而不仅仅是使用助手应用程序唤醒设备。 和 |
|
| 使用 | 例如 ‘1111’ |
| 自动初始化被测应用。 如果这是 |
|
| 跳过开始捕获logcat。它可以提高网络等性能。与日志相关的命令不起作用。默认值为 |
|
| 安装apks之前,软件包列表或 | 例如 |
| 如果值为 |
|
| 设置远程高速缓存的apk的最大数量(默认为10),这些数量将被推送到被测设备的本地存储。 当使用同一组apk时,通过远程缓存apk可以避免每次需要重新安装apk都将其推送到远程文件系统时,可以加快顺序测试用例的执行速度。 将此功能设置为 | 例如 |
| 将Android | 例如 |
| 允许在横向设备上正确处理方向。设置为 |
|
| 默认情况下,如果被测设备上已经存在此应用程序的更新或相同版本,则跳过该应用程序的安装。将此选项设置为 |
|
| 自Appium 1.18.0+,忽略 |
|
| 自Appium1.18.0+,设置应用程序的程序包标识符,用作系统模拟位置。此功能对模拟器没有影响。如果该参数设置为 | 例如 |
| 自Appium 1.18.0,设置logcat消息的输出格式 。 支持的格式见 此文. 详见更多logcat#outputFormat。默认值为 | 例如 |
| 自Appium 1.18.0,为logcat消息设置输出过滤规则。详见更多logcat#filteringOutput。 使用Logcat编写和查看日志 | 例如 |
iOS 独有
这些参数在XCUITest Driver上可用。
键 | 描述 | 值 |
---|---|---|
| (仅支持模拟器) 为iOS的模拟器设置日历格式 | 例如 |
| 被测应用的 bundle ID 。用于在真实设备中启动测试,也用于使用其他需要 bundle ID 的关键字启动测试。在使用 bundle ID 在真实设备上执行测试时,你可以不提供 | 例如 |
| 连接的真实设备的唯一设备编号 (Unique device identifier) | 例如 |
| 以毫秒为单位,在 Appium 运行失败之前设置一个等待 instruments 的时间 | 例如 |
| (仅支持模拟器)强制打开或关闭定位服务。默认值是保持当前模拟器的设定. |
|
| (仅支持模拟器)通过修改 plist 文件设定是否允许应用使用定位服务,从而避免定位服务的警告出现。默认值是保持当前模拟器的设定。请注意在使用这个关键字时,你同时需要使用 |
|
| 当警告弹出的时候,都会自动去点接受。包括隐私访问权限的警告(例如 定位,联系人,照片)。默认值为 false。 |
|
| 当警告弹出的时候,都会自动去点取消。包括隐私访问权限的警告(例如 定位,联系人,照片)。默认值为 false。 |
|
| 使用原生 intruments 库(即关闭 instruments-without-delay)。 |
|
| 在Safari中允许"真实的",非基于 javascript 的 web 点击 (tap) 。 默认值: |
|
| 初始化 safari 的时使用的地址。默认是一个本地的欢迎页面 | 例如 |
| (仅支持模拟器)允许 javascript 在 Safari 中创建新窗口。默认保持模拟器当前设置。 |
|
| (仅支持模拟器)阻止 Safari 显示此网站可能存在风险的警告。默认保持浏览器当前设置。 |
|
| (仅支持模拟器)Safari 是否允许链接在新窗口打开。默认保持浏览器当前设置。 |
|
| (仅支持模拟器)当 Appium 会话开始/结束时是否保留存放密码存放记录 (keychains) 库(Library)/钥匙串(Keychains)) |
|
| 从哪里查找本地化字符串。默认值为 |
|
| 通过 instruments 传递到 AUT 的参数 | 例如 |
| 以毫秒为单位,按下每一个按键之间的延迟时间 | 例如 |
| 是否在 Appium 的日志中显示设备的日志。默认值为 |
|
| 输入文字到文字框的策略。模拟器默认值: |
|
| 以秒为单位,生成屏幕截图的最长等待时间。默认值为:10 | 例如 |
| 用于判断 "应用是否被启动” 的 iOS 自动化脚本代码。默认情况下系统等待直到页面内容非空。结果必须是布尔类型。 | 例如 |
| 用于获取 webview 失败时,发送连接信息到远程调试器的次数。默认次数为: | 例如 |
| 被测应用的名字。 用于支持 iOS 9 以上系统的应用的自动化。 | 例如 |
| (仅支持模拟器) 给模拟器添加一个 SSL 证书。 | 例如 |
| (仅支持真机) 设置等待Safari会话中WebKit响应的时间(以毫秒为单位)。 默认值为 | 例如 |
| (仅支持模拟器,且 <= 11.2) 如果已设置,则Appium 通过本地端口的代理(仅支持模拟器,且 <= 11.2)或者unix套接字上的代理(仅支持模拟器,且 >= 11.3)发送和接收远程调试消息。而不是直接与iOS远程调试器通信。 | 例如 |
| 允许模拟器使用HTTPS在页面上执行异步JavaScript的功能。默认值为 |
|
| 跳过以开始捕获日志,例如崩溃,系统,safari控制台和safari网络。它可能会改善网络性能。与日志相关的命令将不起作用。默认值为 |
|
| (仅支持真机) 使用真机测试,连接 |
|
| 返回有关获取可用上下文命令的上下文详细信息。如果启用了此功能,则返回的上下文列表中的每个项目还将另外包含WebView标题,完整URL和捆绑包标识符。 默认值为 |
|
处理多设备的启动参数
from appium import webdriver
from androguard.core.bytecodes.apk import APK
import os
import random
apk_path = "您的apk的路径"
def get_devices() -> list:
all_devices = []
cmd = "adb devices"
reslut = os.popen(cmd).readlines()[1:]
for item in reslut:
if item != "\n":
all_devices.append(str(item).split("\t")[0])
return all_devices
def getPlatForm(dev: str) -> str:
cmd = 'adb -s {} shell getprop ro.build.version.release'.format(dev)
reslut = os.popen(cmd).readlines()[0]
return str(reslut).split("\n")[0]
def get_apkname(apk):
a = APK(apk, False, "r")
return a.get_package()
def get_apk_lautc(apk):
a = APK(apk, False, "r")
return a.get_main_activity()
def startdevicesApp():
alldevices=get_devices()
if len(alldevices)>0:
for item in alldevices:
desired_caps = {
'platformName': 'Android',
'deviceName': item,
'platformVersion': getPlatForm(item),
'appPackage': get_apkname(apk_path), # 包名
'appActivity': get_apk_lautc(apk_path), # apk的launcherActivity
'skipServerInstallation': True
}
port=random.randint(1000,6000)
driver = webdriver.Remote('http://127.0.0.1:{}/wd/hub'.format(str(port)), desired_caps)
批量启动多个appium服务(待验证!!!)
# -*- coding: utf-8 -*-
import platform
import random
from concurrent.futures import ProcessPoolExecutor, as_completed
import time
import subprocess
import requests
def run_server(cmd):
subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0,
close_fds=True)
def check_server(url):
for _ in range(10):
time.sleep(1)
try:
response = requests.get(url, timeout=5)
if response.status_code // 100 == 2:
return True
except:
pass
return False
def start(port_list: list):
with ProcessPoolExecutor(max_workers=len(port_list)) as executor:
futures = []
for port in port_list:
cmd = f"appium -p {port} --log /Users/lileilei/Desktop/testplan/newon/log/{port}.log"
if platform.system() == "Windows":
futures.append(executor.submit(run_server, cmd))
else:
futures.append(
executor.submit(run_server, cmd).add_done_callback(lambda _: print("----server启动成功---")))
url = f"http://127.0.0.1:{port}/wd/hub/status"
futures.append(executor.submit(check_server, url))
for future in as_completed(futures):
result = future.result()
if isinstance(result, bool):
print(result)
def stop_server(port_list: list):
sysstr = platform.system()
if sysstr == 'Windows':
subprocess.Popen("taskkill /f /im node.exe", shell=True)
else:
for port in port_list:
cmd = f"lsof -i :{port}"
plist = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.readlines()
if len(plist) > 1:
plist_tmp = plist[1].split()
pid = plist_tmp[1]
subprocess.Popen(f"kill -9 {pid}", shell=True)
if __name__ == '__main__':
port_list = []
for dev in range(3):
port = str(random.randint(5641, 5646))
port_list.append(port)
start(port_list)
try:
stop_server(port_list)
except Exception as e:
print("关闭服务失败,原因:%s" % e)
finally:
pass
这里dev在实际过程中可以通过遍历设备列表即get_devices()方法获取
评论