You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebdriverIO多模拟器双应用自动化执行异常求助(含Session错误)

问题:WebdriverIO多远程模式下双安卓模拟器无法分别运行两款APP

我有客户端APP和拣货员端APP两款应用,业务场景是客户提交订单后,拣货员APP收到通知,确认后客户订单状态变更。尝试用WebdriverIO自动化该场景时,无法让两款APP分别在两台安卓模拟器上运行——两款APP都打开在同一台模拟器中,执行顺序混乱,最终抛出invalid session id错误。

错误日志

in "0: Given A user is on the toters app home tab"invalid session id: The session identified by 6bb7ce21-dcdd-49ee-b5b3-bd9b56abb8a1 is not known
at getErrorFromResponseBody (file:///C:/Users/Toters/Desktop/toters-automated-tests/node_modules/webdriver/build/utils.js:195:12)
at NodeJSRequest._request (file:///C:/Users/Toters/Desktop/toters-automated-tests/node_modules/webdriver/build/request/index.js:193:23)
at processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Browser.wrapCommandFn (file:///C:/Users/Toters/Desktop/toters-automated-tests/node_modules/@wdio/utils/build/shim.js:90:29)
at async Browser.$ (file:///C:/Users/Toters/Desktop/toters-automated-tests/node_modules/webdriverio/build/commands/browser/$.js:74:17)
at async Browser.wrapCommandFn (file:///C:/Users/Toters/Desktop/toters-automated-tests/node_modules/@wdio/utils/build/shim.js:90:29)
at async Common.accessHomePage (file:///C:/Users/Toters/Desktop/toters-automated-tests/features/pageobjects/mobile/android/common/common.ts:29:18)
at async World.<anonymous> (file:///C:/Users/Toters/Desktop/toters-automated-tests/features/step-definitions/mobile/android/dark-store/order.ts:10:5)
at async wrapPromiseWithTimeout (C:\Users\Toters\Desktop\toters-automated-tests\node_modules@cucumber\cucumber\src\time.ts:55:10)
at async Object.run (C:\Users\Toters\Desktop\toters-automated-tests\node_modules@cucumber\cucumber\src\user_code_runner.ts:86:16)
[0-0] 2024-05-02T16:01:33.285Z INFO webdriver: COMMAND deleteSession()
[0-0] 2024-05-02T16:01:33.285Z INFO webdriver: COMMAND deleteSession()
[0-0] 2024-05-02T16:01:33.286Z INFO webdriver: [DELETE] http://0.0.0.0:4723/session/9bf0b941-f990-41bb-804e-afca003d57fe
[0-0] 2024-05-02T16:01:33.286Z INFO webdriver: [DELETE] http://0.0.0.0:4723/session/de7d06d8-b0e7-4752-9021-ecd7dd50ebee
[0-0] 2024-05-02T16:01:33.786Z INFO webdriver: RESULT null
[0-0] 2024-05-02T16:01:33.863Z INFO webdriver: RESULT null
[0-0] FAILED in MultiRemote - file:///C:/Users/Toters/Desktop/toters-automated-tests/features/features/mobile/dark-store/place.order.feature
2024-05-02T16:01:33.987Z INFO @wdio/cli:launcher: Run onWorkerEnd hook
2024-05-02T16:01:33.987Z INFO @wdio/cli:launcher: Run onComplete hook

环境配置

  • 两台安卓模拟器(customer-app、picker-app)已启动运行
  • Appium服务在4723端口启动
  • 4728、4729端口空闲
  • android.conf.ts中的capabilities配置:
capabilities: {
    customerApp: {
      capabilities: {
        platformName: "Android",
        "appium:app":
          "/Users/Toters/Desktop/toters-automated-tests/apps/app-staging-debug.apk",
        "appium:platformVersion": "11",
        "appium:deviceName": "customer-app",
        "appium:automationName": "UIAutomator2",
        "appium:systemPort": 4728, // It's good to specify different system ports if running multiple sessions
      },
    },
    pickerApp: {
      capabilities: {
        platformName: "Android",
        "appium:app":
          "/Users/Toters/Desktop/toters-automated-tests/apps/picker-staging-build.apk",
        "appium:platformVersion": "11",
        "appium:deviceName": "picker-app",
        "appium:automationName": "UIAutomator2",
        "appium:systemPort": 4729, // Example of a different system port
      },
    },
  },
  • 修改的类型文件:
    • node_modules@wdio\types\build\index.d.ts:
    declare global {    
        namespace WebdriverIO {            
            interface MultiRemoteBrowser {                
                myAppiumDriver: WebdriverIO.Browser                
                myChromeDriver: WebdriverIO.Browser            
            }        
            interface MochaOpts {            
                [key: string]: any;        
            }        
            interface JasmineOpts {            
                [key: string]: any;        
            }        
            interface CucumberOpts {            
                [key: string]: any;        
            }        
            interface ServiceOption extends Services.ServiceOption {        
            }        
            interface ReporterOption extends Reporters.Options {        
            }        
            interface Config extends Options.Testrunner {        
            }        
            interface HookFunctionExtension {        
            }        
            interface WDIODevtoolsOptions {        
            }        
            interface WDIOVSCodeServiceOptions {        
            }        
            interface BrowserRunnerOptions {        
            }        
            interface ChromedriverOptions extends DriverOptions {        
            }        
            interface GeckodriverOptions extends DriverOptions {        
            }        
            interface EdgedriverOptions extends DriverOptions {        
            }        
            interface SafaridriverOptions {        
            }    
        } 
    }
    
    • node_modules\webdriverio\build\types.d.ts:
    interface MultiRemoteBrowser extends MultiRemoteBrowserType { customerApp: WebdriverIO.Browser; pickerApp: WebdriverIO.Browser; }
    
  • common.ts代码:
const { customerApp, pickerApp } = multiremotebrowser;
  • 启动双设备的函数:
async accessHomePage() {
    while (
      !(await customerApp
        .$("[resource-id = 'com.toters.customer.staging:id/btnGetStarted']")
        .isExisting())
    ) {
      console.log("waiting for get started button to appear");
    }
    while (
      !(await pickerApp
        .$("[resource-id = 'com.toters.customer.staging:id/btnGetStarted']")
        .isExisting())
    ) {
      console.log("waiting for get started button to appear");
    }
  }

解决方案

1. 添加UDID精准匹配模拟器

仅靠deviceName无法让Appium精准区分模拟器,必须添加设备UDID:

  • 运行adb devices获取两台模拟器的设备ID(如emulator-5554、emulator-5556)
  • 在每个capability中添加"appium:udid"参数:
customerApp: {
  capabilities: {
    // ...其他配置
    "appium:udid": "emulator-5554" // 替换为customer-app模拟器的UDID
  }
},
pickerApp: {
  capabilities: {
    // ...其他配置
    "appium:udid": "emulator-5556" // 替换为picker-app模拟器的UDID
  }
}

2. 用自定义类型扩展替代修改node_modules

直接修改依赖包内的类型文件会在依赖更新后失效,改用项目内自定义类型:

  • 在项目根目录创建types/wdio.d.ts:
declare global {
  namespace WebdriverIO {
    interface MultiRemoteBrowser {
      customerApp: WebdriverIO.Browser;
      pickerApp: WebdriverIO.Browser;
    }
  }
}

export {};
  • 在tsconfig.json的include数组中添加该文件:
{
  "include": [
    "./types/**/*.d.ts",
    // ...其他原有文件
  ]
}

删除之前对node_modules中类型文件的修改。

3. 修复拣货员APP的元素定位包名

accessHomePage函数中拣货员APP的资源ID包名错误,使用了客户端APP的包名,导致元素查找失败:

async accessHomePage() {
    // 客户端APP等待元素
    while (!(await customerApp.$("[resource-id='com.toters.customer.staging:id/btnGetStarted']").isExisting())) {
      console.log("Waiting for customer app get started button");
      await customerApp.pause(500); // 添加短暂等待避免循环过于频繁
    }

    // 拣货员APP等待元素(修正为拣货员APP的包名)
    while (!(await pickerApp.$("[resource-id='com.toters.picker.staging:id/btnGetStarted']").isExisting())) {
      console.log("Waiting for picker app get started button");
      await pickerApp.pause(500);
    }
  }

4. 明确指定驱动实例操作

所有测试操作必须通过customerApp或pickerApp调用,禁止使用默认的browser对象,避免会话混淆。

5. 添加Bootstrap端口避免冲突

除了systemPort,额外指定appium:bootstrapPort(UIAutomator2通信端口),防止端口冲突:

customerApp: {
  capabilities: {
    // ...其他配置
    "appium:bootstrapPort": 4748,
  }
},
pickerApp: {
  capabilities: {
    // ...其他配置
    "appium:bootstrapPort": 4749,
  }
}

6. 开启日志调试会话创建

在wdio.conf.ts中设置日志级别为debug,查看会话绑定详情:

export const config: WebdriverIO.Config = {
  // ...其他配置
  logLevel: 'debug',
  // ...
};

启动测试后查看Appium日志,确认两个会话是否分别绑定到目标模拟器。

内容的提问来源于stack exchange,提问作者Fady Chebly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:30:00