如何使用 webdriver.io 控制 Android 设备

Posted

技术标签:

【中文标题】如何使用 webdriver.io 控制 Android 设备【英文标题】:How to control Android device with webdriver.io 【发布时间】:2022-01-15 01:47:38 【问题描述】:

我正在使用appium-uiautomator2-driver 使用以下代码在 android 设备上启动服务器

const startServer = require('appium-uiautomator2-driver')
startServer()
const  AndroidUiautomator2Driver  = require("appium-uiautomator2-driver");

const startDriver = async () => 
  const driver = new AndroidUiautomator2Driver();
  const capabilities = 
    automationName: "UiAutomator2",
    platformName: "Android",
    deviceName: "emulator-5554",
  ;
  await driver.createSession(capabilities);
;
startDriver().catch((e) => console.error(e));

然后我尝试使用 webdriver.io 连接到设备上运行的服务器,如下所示:

const wdio = require("webdriverio");

const driverPromise = wdio.attach(
  isAndroid: true,
  port: 8213,
  isMobile: true,
  automationName: 'UiAutomator2',
  sessionId: "56667cc9-625e-4e13-8c4d-354d1928d859",
);

driverPromise.then(async function (driver) 
  await driver.touchAction([
     action: "press", x: 200, y: 200 ,
     action: "moveTo", x: 200, y: 300 ,
    "release",
  ]);
);

但我得到的是 UnknownCommandException:

2021-12-09T18:01:20.083Z ERROR webdriver: Request failed with status 404 due to unknown command: The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource
(node:61813) UnhandledPromiseRejectionWarning: io.appium.uiautomator2.common.exceptions.UnknownCommandException: The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource
    at io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:75)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
    at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:102)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
    at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:435)
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
    at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:250)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
    at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:266)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:345)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1294)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:352)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:911)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:611)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:552)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:466)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:438)
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:140)
    at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
    at java.lang.Thread.run(Thread.java:919)

谁能告诉我问题出在哪里? UIAutomator 驱动程序是否与 webdriver.io 不兼容?应该是,因为readme.md 表示“驱动程序在 W3C WebDriver 协议的范围内运行,具有几个自定义扩展以涵盖操作系统特定的场景。”

【问题讨论】:

【参考方案1】:

我建议不要直接使用 uiautomator2,因为它被设计为 Appium 的内部部分。

Webdriver.io 支持 Appium 协议。如果相应的automationName 能力将被传递,则可以使用 uiautomator2。 https://github.com/webdriverio/appium-boilerplate/blob/main/config/wdio.android.app.conf.ts

请看android原生项目模板: https://github.com/webdriverio/appium-boilerplate/.

【讨论】:

没有设法让 webdriver.io 与 appium 服务器一起工作。甚至不使用样板文件中的配置。 “直接使用uiautomator2”是什么意思? 您正在尝试使用 uiautomator2-driver lib 创建一个单独的 uiautomator2 驱动程序会话,然后附加。我建议使用 wdio.remote 创建一个具有“UIAutomator2”功能的新 appium 会话,如下例所示:appium.io/docs/en/about-appium/getting-started/index.html

以上是关于如何使用 webdriver.io 控制 Android 设备的主要内容,如果未能解决你的问题,请参考以下文章

Selenium & webdriver.io 如何使用 executeScript?

Webdriver.io - 如何在配置中使用 beforeEach 钩子

如何使用没有标识符的 WebDriver.io 在页面上查找元素

如何使用 webdriver.io 模拟 ctrl-click 或 shift-click?

如何在 webdriver.io 中以 32 位模式启动 IE

如何等待 WEBdriver.io 的 Ajax 调用完成