View a markdown version of this page

使用 Appium 与设备互动 - AWS Device Farm

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 Appium 与设备互动

创建远程访问会话后,该设备将可用于 Appium 测试。在整个远程访问会话期间,你可以在设备上随心所欲地运行任意数量的 Appium 会话,对使用的客户端没有限制。例如,你可以先使用 IDE 中的本地 Appium 代码运行测试,然后切换到使用 Appium Inspector 来解决遇到的任何问题。会话最多可持续 150 分钟,但是,如果超过 5 分钟没有活动(通过交互式控制台或 Appium 端点),则会话将超时。

在 Appium 会话中使用应用程序进行测试

有几种方法可以为你的 Appium 会话提供应用程序:

  • 将应用程序上传到 Device Farm 并将其安装在会话中。

  • 指定 HTTPS 网址或 Amazon S3 URI 作为appium:app功能。

  • 按软件包名称引用已安装的应用程序(appium:appPackage在 Android 或 appium:bundleId iOS 上使用)。

  • 通过指定browserName功能来测试网页应用程序(Chrome在 Android 上、iOS Safari 上)。

标准应用程序大小限制 (4 GB) 适用于所有应用程序来源。

注意

Device Farm 不支持在远程访问会话appium:app期间传入本地文件系统路径。

上传、安装和使用应用程序

要在 Appium 会话中使用已上传的应用程序,请按照以下步骤操作:

  1. 上传并安装您的应用程序

    有两种方法可以将应用程序上传并安装到被测设备上:

    • 在您的CreateRemoteAccessSession请求中包含应用程序 ARN。会话开始时,应用程序会自动安装到设备上。您还可以添加辅助应用程序 ARNs,该应用程序将与主应用程序一起安装。

    • 在活动会话期间使用 InstallToRemoteAccessSessionAPI 安装应用程序,或者通过 Device Farm 控制台上传应用程序。这样,您就可以在不创建新会话的情况下更改被测应用程序。

  2. 使用已安装的应用程序

    安装后,该应用程序将自动注入为任何后续的 Appium 会话的默认appium:app功能。如果您添加了辅助应用程序,则它们将被设置为appium:otherApps功能。

    例如,如果您使用com.aws.devicefarm.sample作为应用程序和com.aws.devicefarm.other.sample辅助应用程序之一来创建远程访问会话,那么当您开始创建 Appium 会话时,它将具有类似于以下内容的功能:

    { "value": { "sessionId": "abcdef123456-1234-5678-abcd-abcdef123456", "capabilities": { "app": "/tmp/com.aws.devicefarm.sample.apk", "otherApps": "[\"/tmp/com.aws.devicefarm.other.sample.apk\"]", ... } } }

    如果您在会话期间安装新应用程序,它将取代当前appium:app功能。如果先前安装的应用程序具有不同的软件包名称,则它会保留在设备上并移至该appium:otherApps功能。

    例如,如果您最初在创建远程访问会话com.aws.devicefarm.sample时使用,但随后在会话com.aws.devicefarm.other.sample期间安装,则您的 Appium 会话将具有类似于以下内容的功能:

    { "value": { "sessionId": "abcdef123456-1234-5678-abcd-abcdef123456", "capabilities": { "app": "/tmp/com.aws.devicefarm.other.sample.apk", "otherApps": "[\"/tmp/com.aws.devicefarm.sample.apk\"]", ... } } }
注意

有关在远程访问会话中自动上传应用程序的更多信息,请参阅自动上传应用程序

使用 HTTPS 网址

创建 Appium 会话时,您可以将可公开访问的 HTTPS 网址指定为appium:app所需的功能。网址必须直接指向可下载的应用程序文件(例如,.apk.ipa文件)。Device Farm 从指定的网址下载应用程序,并将其安装到被测设备上。

重要

仅支持 HT URLs TPS。纯HTTP URLs 被拒绝。

例如,以下 Appium 会话创建请求从 HTTPS 网址下载应用程序:

{ "capabilities": { "alwaysMatch": {}, "firstMatch": [ { "appium:app": "https://example.com/path/to/MyApp.apk" } ] } }

使用亚马逊 S3 URI

创建 Appium 会话时,您可以将 Amazon S3 URI(例如s3://my-bucket/path/to/MyApp.ipa)指定为appium:app所需的功能。Device Farm 从指定的 S3 位置下载应用程序并将其安装到被测设备上。

要使用 S3 URI,必须满足以下要求:

  • 远程访问会话必须从配置了 IAM 执行角色的项目启动。

  • IAM 执行角色的最大会话持续时间必须至少为 150 分钟,因为该角色是在远程访问会话期间担任的。

  • IAM 执行角色必须有权调用 s3:GetObject URI 中指定的 S3 对象。我们还建议对同一对象授予s3:HeadObject权限,这样 Device Farm 就可以在尝试下载之前验证该对象的存在。

例如,以下 Appium 会话创建请求从 S3 URI 下载应用程序:

{ "capabilities": { "alwaysMatch": {}, "firstMatch": [ { "appium:app": "s3://my-test-bucket/apps/MyApp.ipa" } ] } }

以下是 IAM 权限策略示例,该策略授予从 Amazon S3 下载应用程序的推荐访问权限,包括可选s3:HeadObject权限。有关配置 IAM 执行角色的更多信息,请参阅使用 IAM 执行角色访问 AWS 资源

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:HeadObject" ], "Resource": "arn:aws:s3:::my-test-bucket/apps/*" } ] }

使用已安装的应用程序

如果您要测试的应用程序已安装在设备上,则可以直接通过其软件包名称来引用它,而不必上传它。在 Android 上使用appium:appPackageappium:appActivity功能,或者在 iOS 上使用该appium:bundleId功能。

例如,以下 Appium 会话创建请求会启动已经安装的安卓应用程序:

{ "capabilities": { "alwaysMatch": {}, "firstMatch": [ { "appium:appPackage": "com.example.myapp", "appium:appActivity": "com.example.myapp.MainActivity" } ] } }

在 iOS 上,请appium:bundleId改用:

{ "capabilities": { "alwaysMatch": {}, "firstMatch": [ { "appium:bundleId": "com.example.myapp" } ] } }

测试 Web 应用程序

要测试 Web 应用程序,请在您的 Appium 会话创建请求中指定该browserName功能。Chrome在安卓设备或 iOS 设备Safari上使用。

例如,以下请求会在安卓设备上打开 Chrome:

{ "capabilities": { "alwaysMatch": {}, "firstMatch": [ { "browserName": "Chrome" } ] } }

如何使用 Appium 端点

以下是从控制台、和访问会话的 Appium 端点的 AWS CLI步骤。 AWS SDKs这些步骤包括如何开始使用各种 Appium 客户端测试框架运行测试:

Console
  1. 在 Web 浏览器中打开远程访问会话页面:

    远程访问会话页面
  2. 要使用 Appium Inspector 运行会话,请执行以下操作:

    1. 点击按钮设置 Appi um 会话

    2. 按照页面上的说明进行操作,了解如何使用 Appium Inspector 启动会话。

  3. 要从本地 IDE 运行 Appium 测试,请执行以下操作:

    1. 点击文本 Appium 端点网址旁边的 “复制” 图标

    2. 将此 URL 粘贴到您当前指定远程地址或命令执行器的本地 Appium 代码中。要查看特定语言的示例,请单击此示例窗口中您选择的语言的选项卡。

AWS CLI

首先, up-to-date通过下载并安装最新版本来验证您的 AWS CLI 版本是否为最新版本

重要

Appium 终端节点字段在旧版本的 AWS CLI 中不可用。

会话启动并运行后,Appium 端点网址将通过响应 API 调用时命名的remoteDriverEndpoint字段提供:GetRemoteAccessSession

$ aws devicefarm get-remote-access-session \ --arn "arn:aws:devicefarm:us-west-2:123456789876:session:abcdef123456-1234-5678-abcd-abcdef123456/abcdef123456-1234-5678-abcd-abcdef123456/00000"

这将显示如下输出:

{ "remoteAccessSession": { "arn": "arn:aws:devicefarm:us-west-2:111122223333:session:abcdef123456-1234-5678-abcd-abcdef123456/abcdef123456-1234-5678-abcd-abcdef123456/00000", "name": "Google Pixel 8", "status": "RUNNING", "endpoints": { "remoteDriverEndpoint": "https://devicefarm-interactive-global.us-west-2.api.aws/remote-endpoint/ABCD1234...", ... }

无论您当前指定远程地址或命令执行器,都可以在本地 Appium 代码中使用此 URL。要查看特定语言的示例,请单击此示例窗口中您选择的语言的选项卡。

有关如何直接从命令行与端点交互的示例,您可以使用命令行工具 curl 直接调用端点: WebDriver

$ curl "https://devicefarm-interactive-global.us-west-2.api.aws/remote-endpoint/ABCD1234.../status"

这将显示如下输出:

{ "value": { "ready": true, "message": "The server is ready to accept new connections", "build": { "version": "2.5.1" } } }
Python

会话启动并运行后,Appium 端点网址将通过响应 API 调用时命名的remoteDriverEndpoint字段提供:GetRemoteAccessSession

# To get the URL import sys import boto3 from botocore.exceptions import ClientError def get_appium_endpoint() -> str: session_arn = "arn:aws:devicefarm:us-west-2:111122223333:session:abcdef123456-1234-5678-abcd-abcdef123456/abcdef123456-1234-5678-abcd-abcdef123456/00000" device_farm_client = boto3.client("devicefarm", region_name="us-west-2") try: resp = device_farm_client.get_remote_access_session(arn=session_arn) except ClientError as exc: sys.exit(f"Failed to call Device Farm: {exc}") remote_access_session = resp.get("remoteAccessSession", {}) endpoints = remote_access_session.get("endpoints", {}) endpoint = endpoints.get("remoteDriverEndpoint") if not endpoint: sys.exit("Device Farm response did not include endpoints.remoteDriverEndpoint") return endpoint # To use the URL from appium import webdriver from appium.options.android import UiAutomator2Options opts = UiAutomator2Options() driver = webdriver.Remote(get_appium_endpoint(), options=opts) # ... driver.quit()
Java

注意:此示例使用 AWS 适用于 Java v2 的 SDK,并且与 JDK 版本 11 及更高版本兼容。

会话启动并运行后,Appium 端点网址将通过响应 API 调用时命名的remoteDriverEndpoint字段提供:GetRemoteAccessSession

// To get the URL import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.devicefarm.DeviceFarmClient; import software.amazon.awssdk.services.devicefarm.model.GetRemoteAccessSessionRequest; import software.amazon.awssdk.services.devicefarm.model.GetRemoteAccessSessionResponse; public class AppiumEndpointBuilder { public static String getAppiumEndpoint() throws Exception { String session_arn = "arn:aws:devicefarm:us-west-2:111122223333:session:abcdef123456-1234-5678-abcd-abcdef123456/abcdef123456-1234-5678-abcd-abcdef123456/00000"; try (DeviceFarmClient client = DeviceFarmClient.builder() .region(Region.US_WEST_2) .credentialsProvider(DefaultCredentialsProvider.create()) .build()) { GetRemoteAccessSessionResponse resp = client.getRemoteAccessSession( GetRemoteAccessSessionRequest.builder().arn(session_arn).build() ); String endpoint = resp.remoteAccessSession().endpoints().remoteDriverEndpoint(); if (endpoint == null || endpoint.isEmpty()) { throw new IllegalStateException("remoteDriverEndpoint missing from response"); } return endpoint; } } } // To use the URL import io.appium.java_client.android.AndroidDriver; import io.appium.java_client.android.options.UiAutomator2Options; import java.net.URL; public class ExampleTest { public static void main(String[] args) throws Exception { String endpoint = AppiumEndpointBuilder.getAppiumEndpoint(); UiAutomator2Options options = new UiAutomator2Options(); AndroidDriver driver = new AndroidDriver(new URL(endpoint), options); try { // ... your test ... } finally { driver.quit(); } } }
JavaScript

注意:此示例使用 AWS 适用于 v JavaScript 3 的 SDK,使用 Node 18+ 的 WebDriverIO v8+。

会话启动并运行后,Appium 端点网址将通过响应 API 调用时命名的remoteDriverEndpoint字段提供:GetRemoteAccessSession

// To get the URL import { DeviceFarmClient, GetRemoteAccessSessionCommand } from "@aws-sdk/client-device-farm"; export async function getAppiumEndpoint() { const sessionArn = "arn:aws:devicefarm:us-west-2:111122223333:session:abcdef123456-1234-5678-abcd-abcdef123456/abcdef123456-1234-5678-abcd-abcdef123456/00000"; const client = new DeviceFarmClient({ region: "us-west-2" }); const resp = await client.send(new GetRemoteAccessSessionCommand({ arn: sessionArn })); const endpoint = resp?.remoteAccessSession?.endpoints?.remoteDriverEndpoint; if (!endpoint) throw new Error("remoteDriverEndpoint missing from response"); return endpoint; } // To use the URL with WebdriverIO import { remote } from "webdriverio"; (async () => { const endpoint = await getAppiumEndpoint(); const u = new URL(endpoint); const driver = await remote({ protocol: u.protocol.replace(":", ""), hostname: u.hostname, port: u.port ? Number(u.port) : (u.protocol === "https:" ? 443 : 80), path: u.pathname + u.search, capabilities: { platformName: "Android", "appium:automationName": "UiAutomator2", // ...other caps... }, }); try { // ... your test ... } finally { await driver.deleteSession(); } })();
C#

会话启动并运行后,Appium 端点网址将通过响应 API 调用时命名的remoteDriverEndpoint字段提供:GetRemoteAccessSession

// To get the URL using System; using System.Threading.Tasks; using Amazon; using Amazon.DeviceFarm; using Amazon.DeviceFarm.Model; public static class AppiumEndpointBuilder { public static async Task<string> GetAppiumEndpointAsync() { var sessionArn = "arn:aws:devicefarm:us-west-2:111122223333:session:abcdef123456-1234-5678-abcd-abcdef123456/abcdef123456-1234-5678-abcd-abcdef123456/00000"; var config = new AmazonDeviceFarmConfig { RegionEndpoint = RegionEndpoint.USWest2 }; using var client = new AmazonDeviceFarmClient(config); var resp = await client.GetRemoteAccessSessionAsync(new GetRemoteAccessSessionRequest { Arn = sessionArn }); var endpoint = resp?.RemoteAccessSession?.Endpoints?.RemoteDriverEndpoint; if (string.IsNullOrWhiteSpace(endpoint)) throw new InvalidOperationException("RemoteDriverEndpoint missing from response"); return endpoint; } } // To use the URL using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Android; class Example { static async Task Main() { var endpoint = await AppiumEndpointBuilder.GetAppiumEndpointAsync(); var options = new AppiumOptions(); options.PlatformName = "Android"; options.AutomationName = "UiAutomator2"; using var driver = new AndroidDriver(new Uri(endpoint), options); try { // ... your test ... } finally { driver.Quit(); } } }
Ruby

会话启动并运行后,Appium 端点网址将通过响应 API 调用时命名的remoteDriverEndpoint字段提供:GetRemoteAccessSession

# To get the URL require 'aws-sdk-devicefarm' def get_appium_endpoint session_arn = "arn:aws:devicefarm:us-west-2:111122223333:session:abcdef123456-1234-5678-abcd-abcdef123456/abcdef123456-1234-5678-abcd-abcdef123456/00000" client = Aws::DeviceFarm::Client.new(region: 'us-west-2') resp = client.get_remote_access_session(arn: session_arn) endpoint = resp.remote_access_session.endpoints.remote_driver_endpoint raise "remote_driver_endpoint missing from response" if endpoint.nil? || endpoint.empty? endpoint end # To use the URL require 'appium_lib_core' endpoint = get_appium_endpoint opts = { server_url: endpoint, capabilities: { 'platformName' => 'Android', 'appium:automationName' => 'UiAutomator2' } } driver = Appium::Core.for(opts).start_driver begin # ... your test ... ensure driver.quit end