非常感谢梦无矶大佬的分享,go-iOS 方案我们团队还在调研中,大家可以根据大佬的分享,先行部署实现 iOS17 与 iOS18 的测试方案。
文章转载于:https://mp.weixin.qq.com/s/UJhd3mFwh2jLliY7KJA05A,已获得作者授权

目录:

一、go-ios 安装部署启动

    1、Windows 系统驱动下载

    2、Windows 系统驱动部署

    3、配置环境变量

二、构建 wda.ipa

    方案一:通过 xcode 构建

    方案二:通过命令构建 wda.ipa

    可能遇到的问题及解决方案

三、IOS18 启动自动化测试

四、连接到 AirtestIDE

五、封装 IOS17+ 的启动方法总结

    go-ios 的基本使用

之前写过 IOS17 打包 WebDriverAgent 构建 ipa 包安装到 ios16.7 及 ios17 以上设备结合 tidevice3 进行自动化测试,但那种方式发现在不同的设备上会有失败的概率,小版本号也会有一定影响,而且操作比较繁琐,失败率较高。

于是乎在 tidevice 作者的推荐下,尝试使用 go-ios 这个开源项目进行启动 wda 程序,发现稳定性很好,ios17 以上,包括 ios18(目前最新),均可以正常启动 wda,同时 Airtest 也可以通过 ubsmax 连接 IOS17 及 IOS18 设备进行控件树识别,进行自动化测试。

代码层面,目前 Airtest 启动 wda 是采用的 tidevice,所以需要自己改动源码或者重新封装 go-ios 的方式进行启动,Airtest 官方目前也在调研 go-ios,后续应该会出相应解决 ios17、ios18 启动 wda 的方案,本文也会提供封装源码。

一、go-ios 安装部署启动

项目地址:https://github.com/danielpaulus/go-ios

releases 包下载:https://github.com/danielpaulus/go-ios/releases

这个项目也可以使用如下命令进行安装:

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

npm install -g go-ios

releases 包可以根据自己的系统进行下载:

1、Windows 系统驱动下载

我使用的是 windows,非 Windows 系统可以跳过这段,通过 README 教程查看,需要下载相关驱动。

npm install -g go-ios can be used to get going. Run ios --help after the installation for details. For iOS 17+ devices you need to run sudo ios tunnel start for go ios to work. This will start a tunnel daemon. To make this work on Windows, download the latest wintun.dll from here https://git.zx2c4.com/wintun and copy it to C:/Windows/system32

翻译:

npm install -g go-ios 下载后可以进行运行。安装后运行 ios --help 了解详细信息。对于 iOS 17+ 设备,您需要运行 sudo ios tunnel start 才能使 go ios 正常工作。这将启动一个 tunnel 守护程序。要在 Windows 上执行此操作,请从此处下载最新的wintun.dll https://git.zx2c4.com/wintun 并将其复制到 C:/Windows/system32

驱动网址 1:https://www.wintun.net/

驱动网址 2:https://git.zx2c4.com/wintun

2、Windows 系统驱动部署

1、把下载好的驱动文件进行解压缩,进入bin\amd64(现在的 windows 系统电脑一般是 amd64),把wintun.dll文件复制到C:/Windows/system32 根目录下。

Ubuntu\Linux 查看系统内核

2、注意:是把 dll 文件放过去,不是整个文件夹放过去

3、配置环境变量

1、如果是通过 npm -g 安装的就不需要进行这一步,如果是通过 release 包进行下载的,想要在全局使用 go-ios,则需要配置环境变量。

2、比如我下载后存放的路径是D:\P_Program\go-ios-win\ios.exe

3、打开我的电脑 -> 属性 -> 系统 -> 系统信息 -> 高级系统设置 -> 环境变量

4、点开环境变量 -> 系统变量 -> 找到变量为 path 的,双击进入 ->  新建 -> 输入D:\P_Program\go-ios-win\ios.exe -> 确认

5、win+r 输入 cmd,打开控制台,输入 ios 验证是否配置成功,出现如下内容,代表环境变量配置成功。

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

C:\Users>ios
Usage:
  ios activate [options]
  ios listen [options]
  .....

二、构建 wda.ipa

这个在之前的文章有写,这里简单阐述一下步骤,一个是通过 xcode 构建直接安装到设备,一个是通过命令构建 wda.ipa,再把该 ipa 安装到设备。

设备部署构建环境:

方案一:通过 xcode 构建

1、进入 appium-webdriveragent 下载源码:https://github.com/appium/WebDriverAgent/releases

2、使用 xcode16 打开源码目录下的WebDriverAgent.xcodeproj文件,打开后,在左侧选择WebDriverAgentRunner,然后在 Signing & Capabilities 选项卡中,确保 Automatically manage signing 选项已勾选。

3、xcode16 不再像之前的版本一样需要选 ios 小版本,这里只需要勾选 ios 大版本即可,账号使用企业开发者账户签名,个人的只有七天有效期。

4、Bundle identifier 需要更改一下,不重复就可以了,我是直接在后面加点后缀。

5、连接目标 ios 设备,product --> Destination --> 选择对应设备,如果显示设备没有准备好,插拔连接线,还是这样直接重启就可以恢复连接。

6、选择对应方案,Product --> Scheme -- > WebDriverAgentRunner

7、启动构建测试:Product --> Build For --> Testing

8、测试构建:Product --> Test

以上步骤成功标志是:

方案二:通过命令构建 wda.ipa

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

# 1、xcode打开代码下的WebDriverAgent.xcodeproj项目,配置好团队唯一id等信息
$ xcodebuild build-for-testing -scheme WebDriverAgentRunner -sdk iphoneos -configuration Release -derivedDataPath /tmp/derivedDataPath
# 2、cd的目录和上条命令/tmp/derivedDataPath目录是一致的
$ cd /tmp/derivedDataPath
# 3、这个目录是连续的可以和上条命令一起 cd /tmp/derivedDataPath/Build/Products/Release-iphoneos
$ cd Build/Products/Release-iphoneos
# 4、在这个目录下创建一个Payload文件夹,并且把.app文件复制到Payload文件夹下
$ mkdir Payload && mv *.app Payload
# 5、用zip命令打包出来一个ipa
$ zip -r WDA.ipa Payload
# 6、把WDA.ipa安装到指定设备上
$ ios install WDA.ipa --udid=<udid>

打开访达,shift+command+g 在出现的窗口中输入/tmp,斜杆必须写,这样可以进去对应目录吧 WDA.ipa 拖出来。

安装示列:

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios install --path=ios18-WDA.ipa --udid=00008110-00112DA90E07801E

可能遇到的问题及解决方案

使用方案一在进行构建时,可能会出现构建失败,或者在安装 ipa 的时候会出现中断报错,这是因为我们在新的设备上没有信任该开发者账号。(ios17 之前貌似不用)

安装报错示列:

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

{"err":"your app is not properly signed for this device, check your codesigning and provisioningprofile. original error: 'ApplicationVerificationFailed' errorDescription:'Failed to verify code signature of /var/installd/Library/Caches/com.apple.mobile.installd.staging/temp.XyWuva/extracted/Payload/WebDriverAgentRunner-Runner.app : 0xe8008015 (A valid provisioning profile for this executable was not found.)'","level":"fatal","msg":"failed writing","time":"2024-09-14T18:46:41+08:00"}

解决方案:打开设置 -> 通用 -> VPN 与设备管理 -> 信任开发者

三、IOS18 启动自动化测试

1、ios17 以上需要通过 go-ios 启动隧道

以管理员启动终端运行如下命令,且不要关闭该界面。

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios tunnel start

启动之后不要关闭,连接到 ios17 以上设备会自动帮你启动隧道。

2、设置 -> 开发者 -> 启动 UI 自动化勾上,(如果没有打开开发者: 【设置】-【隐私与安全性】 ,拉到底部,就可在“安全性”一栏看到开发者模式了。)

3、指定设备启动 WebDriverAgentRunner

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios runwda --udid=00008110-00112DA90E07801E --bundleid=com.facebook.WebDriverAgentRunnerxzz.xctrunner --testrunnerbundleid=com.facebook.WebDriverAgentRunnerxzz.xctrunner --xctestconfig=WebDriverAgentRunner.xctest

四、连接到 AirtestIDE

1、打开 AirtestIDE

2、通过远程 IOS 连接

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios:///http+usbmux://00008110-00112DA90E07801E

3、点击连接即可,下面是 IOS17.6.1 和 IOS18 使用 go-ios 启动 wda 后连接 AirtestIDE 的展示

IOS18

4、均可以正常连上,后续也使用代码进行测试了,可以正常运行自动化,测试连接设备代码如下(脚本不便展示):

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

from airtest.core.api import *
# ios18设备的udid
devices = '00008110-00112DA90E07801E'
auto_setup(__file__, devices=[f"ios:///http+usbmux://{devices}", ])
from poco.drivers.ios import iosPoco
dev = connect_device(f"iOS:///http+usbmux://{devices}")
poco_ios = iosPoco(device=dev)

五、封装 IOS17+ 的启动方法

Airtest 中的方法 start_app() 是封装的 tidevice,但 tidevice 是不支持 ios17 和 ios18,并且作者开了新的 tidevice3,并且推荐 go-ios,所以可以直接修改源码更换,也可以重新封装,这里我为了不影响 ios17 之前的设备运行,我采用重新封装的方法。封装代码如下:

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

# -*- coding: utf-8 -*-
"""
@Time : 2024/9/13 17:57
@Email : Lvan826199@163.com
@公众号 : 梦无矶测开实录
@File : connect_ios17_or_higher.py
"""
__author__ = "梦无矶小仔"


import os
import subprocess
import time
from airtest.core.api import connect_device, device, sleep, auto_setup, click, text
from poco.drivers.ios import iosPoco


class IOS17HigherAutomation:
    def __init__(self, wda_package_name, uuid, wda_ipa_path, log_dir='./ios'):
        self.wda_package_name = wda_package_name
        self.uuid = uuid
        self.wda_ipa_path = wda_ipa_path
        self.log_dir = log_dir
        self.device = None
        self.poco = None
        self.width = None
        self.height = None

    def install_wda(self):
        os.system(self.wda_ipa_path)
        time.sleep(6)

    def run_wda(self):
        run_wda_command = [
            'ios', 'runwda',
            f'--bundleid={self.wda_package_name}',
            f'--testrunnerbundleid={self.wda_package_name}',
            '--xctestconfig=WebDriverAgentRunner.xctest'
        ]
        subprocess.Popen(run_wda_command)
        time.sleep(6)

    def connect_device(self):
        self.device = connect_device(f"ios:///http+usbmux://{self.uuid}")
        self.poco = iosPoco(device=self.device)
        dev = device()
        self.width, self.height = dev.get_current_resolution()
        auto_setup(logdir=self.log_dir, compress=3, devices=[f"ios:///http+usbmux://{self.uuid}"])
        sleep(6)

    def start_recording(self, fps=4, orientation=1):
        self.device.start_recording(fps=fps, orientation=orientation)

    def stop_recording(self):
        self.device.stop_recording()

    def start_app(self, app_package_name):
        # # you need to kill the app firstly, and then start the app
        # otherwith it would fail to open the app after you open the app more than 188 times
        self._kill_app(app_package_name)
        self._launch_app(app_package_name)

    def _kill_app(self, app_package_name):
        kill_app_command = ['ios', 'kill', app_package_name]
        subprocess.run(kill_app_command, check=True)

    def _launch_app(self, app_package_name):
        launch_app_command = ['ios', 'launch', app_package_name]
        subprocess.run(launch_app_command, check=True)

    def search_in_app_store(self, search_text):
        self.poco(nameMatches=".*search").click()
        self.poco(nameMatches="AppStore.searchField").click()
        text(search_text)
        click([self.width * 0.5, self.height * 0.5])

    def uninstall_wda(self):
        os.system(f'tidevice uninstall {self.wda_package_name}')


def main():
    WDA_PACKAGE_NAME = 'xxx'
    UUID = 'xxx'
    WDA_IPA_PATH = r'ios install C:\xxx\xxxx\xxxx\xxxxx\xxxxx\wda.ipa'

    ios_automation = IOS17HigherAutomation(WDA_PACKAGE_NAME, UUID, WDA_IPA_PATH)

    ios_automation.install_wda()
    ios_automation.run_wda()
    ios_automation.connect_device()
    ios_automation.start_recording()

    ios_automation.start_app("com.apple.AppStore")
    ios_automation.search_in_app_store("原神")

    ios_automation.stop_recording()
    ios_automation.uninstall_wda()


if __name__ == "__main__":
    main()

大家可以根据这个,封装符合自己业务的代码。

总结

1、安装 go-ios

2、使用 xcode 安装 WebDriverAgentRunner

3、使用ios tunnel startios runwda --udid=00008110-00112DA90E07801E --bundleid=com.xxxx.xctrunner --testrunnerbundleid=com.xxxx.xctrunner --xctestconfig=WebDriverAgentRunner.xctest命令启动 WebDriverAgentRunner

4、远程连接 AirtestIDE

go-ios 的基本使用

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios activate [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios listen [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios list [options] [--details]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios info [display | lockdown] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios image list [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios image mount [--path=<imagepath>] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios image unmount [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios image auto [--basedir=<where_dev_images_are_stored>] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios syslog [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios screenshot [options] [--output=<outfile>] [--stream] [--port=<port>]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios instruments notifications [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios crash ls [<pattern>] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios crash cp <srcpattern> <target> [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios crash rm <cwd> <pattern> [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios devicename [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios date [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios timeformat (24h | 12h | toggle | get) [--force] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios devicestate list [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios devicestate enable <profileTypeId> <profileId> [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios erase [--force] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios lang [--setlocale=<locale>] [--setlang=<newlang>] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios mobilegestalt <key>... [--plist] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios diagnostics list [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios profile list [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios prepare [--skip-all] [--skip=<option>]... [--certfile=<cert_file_path>] [--orgname=<org_name>] [--locale] [--lang] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios prepare create-cert

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios prepare printskip

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios profile remove <profileName> [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios profile add <profileFile> [--p12file=<orgid>] [--password=<p12password>] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios httpproxy <host> <port> [<user>] [<pass>] --p12file=<orgid> --password=<p12password> [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios httpproxy remove [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios pair [--p12file=<orgid>] [--password=<p12password>] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios ps [--apps] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios ip [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios forward [options] <hostPort> <targetPort>

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios dproxy [--binary] [--mode=<all(default)|usbmuxd|utun>] [--iface=<iface>] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios readpair [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios pcap [options] [--pid=<processID>] [--process=<processName>]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios install --path=<ipaOrAppFolder> [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios uninstall <bundleID> [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios apps [--system] [--all] [--list] [--filesharing] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios launch <bundleID> [--wait] [--kill-existing] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios kill (<bundleID> | --pid=<processID> | --process=<processName>) [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios runtest [--bundle-id=<bundleid>] [--test-runner-bundle-id=<testrunnerbundleid>] [--xctest-config=<xctestconfig>] [--log-output=<file>] [--xctest] [--test-to-run=<tests>]... [--test-to-skip=<tests>]... [--env=<e>]... [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios runwda [--bundleid=<bundleid>] [--testrunnerbundleid=<testbundleid>] [--xctestconfig=<xctestconfig>] [--log-output=<file>] [--arg=<a>]... [--env=<e>]... [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios ax [--font=<fontSize>] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios debug [options] [--stop-at-entry] <app_path>

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios fsync (rm [--r] | tree | mkdir) --path=<targetPath>

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios fsync (pull | push) --srcPath=<srcPath> --dstPath=<dstPath>

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios reboot [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios -h | --help

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios --version | version [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios setlocation [options] [--lat=<lat>] [--lon=<lon>]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios setlocationgpx [options] [--gpxfilepath=<gpxfilepath>]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios resetlocation [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios assistivetouch (enable | disable | toggle | get) [--force] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios voiceover (enable | disable | toggle | get) [--force] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios zoomtouch (enable | disable | toggle | get) [--force] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios diskspace [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios batterycheck [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios tunnel start [options] [--pair-record-path=<pairrecordpath>] [--userspace]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios tunnel ls [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios tunnel stopagent

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios devmode (enable | get) [--enable-post-restart] [options]

--javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

ios rsd ls [options]


↙↙↙阅读原文可查看相关链接,并与作者交流