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
相关产品推荐
相关产品推荐

