Appium/Webdriverio测试套件:阻止Driver关闭、调整Capabilities及结构优化
appium:noReset配置冲突的问题 1. 阻止Driver在测试用例间关闭(共享Session)
WebdriverIO默认会为每个测试文件启动新的Driver Session,导致配置被重置。要实现Session共享,可通过以下方式操作:
修改WDIO核心配置:在
wdio.conf.ts中禁用自动启动Driver,同时通过全局钩子维护单个Session:// wdio.conf.ts export const config: WebdriverIO.Config = { autoLaunch: false, // 禁止自动启动默认Session maxInstances: 1, // 限制实例数为1,确保单Session运行 services: [ ['appium', { keepAlive: true, // 保持Appium服务不随Session关闭而终止 }] ], // 全局钩子:所有测试执行前启动一次Driver beforeSession: async (config, capabilities) => { global.driver = await remote({ ...config, capabilities: { ...capabilities, // 基础Cap配置,后续用例复用该Session } }); }, // 全局钩子:所有测试结束后统一关闭Driver afterSession: async () => { await global.driver.deleteSession(); } };调整测试文件逻辑:每个测试文件不再单独初始化Driver,直接使用全局
driver,并按需重置应用:// login.ts describe('全新安装登录测试', () => { before(async () => { // 手动重置应用,模拟全新安装效果 await global.driver.executeScript('mobile: resetApp'); }); it('验证首次安装登录流程', async () => { // 登录测试逻辑 }); }); // doActionAfterLogin_A.ts describe('登录后操作测试', () => { it('验证登录后的功能A', async () => { // 直接使用已登录的Session执行测试 }); });注意:这种方式依赖测试执行顺序,需在WDIO配置的
specs数组中指定先执行login.ts再执行doActionAfterLogin_A.ts。
2. 在测试用例间修改Desired Capabilities(独立Session)
Appium的Capabilities是Session启动时确定的,无法在已启动的Session中修改,因此可为每个测试文件单独启动带有对应Cap的Session:
禁用WDIO自动启动:在
wdio.conf.ts中设置autoLaunch: false。每个测试文件手动初始化Driver:
// login.ts import { remote } from 'webdriverio'; describe('全新安装登录测试', () => { let driver: WebdriverIO.Browser; before(async () => { driver = await remote({ hostname: 'localhost', port: 4723, path: '/wd/hub', capabilities: { platformName: 'Android', 'appium:deviceName': 'emulator-5554', 'appium:app': './path/to/app.apk', 'appium:noReset': false, // 其他必要Capabilities } }); }); after(async () => { await driver.deleteSession(); }); it('验证首次安装登录', async () => { // 测试逻辑 }); });// doActionAfterLogin_A.ts import { remote } from 'webdriverio'; describe('登录后操作测试', () => { let driver: WebdriverIO.Browser; before(async () => { driver = await remote({ hostname: 'localhost', port: 4723, path: '/wd/hub', capabilities: { platformName: 'Android', 'appium:deviceName': 'emulator-5554', 'appium:app': './path/to/app.apk', 'appium:noReset': true, // 其他必要Capabilities } }); }); after(async () => { await driver.deleteSession(); }); it('验证登录后功能A', async () => { // 测试逻辑(此时应用保留之前的登录状态) }); });这种方式每个测试文件完全独立,无需依赖执行顺序,但会增加Session启动的时间开销。
3. 测试用例结构优化建议
当前用例结构存在耦合性高的问题:doActionAfterLogin_A.ts依赖login.ts的执行结果,若登录测试失败,后续测试也会失败,不符合测试用例独立性原则。可调整为以下两种结构:
方案A:将登录作为前置步骤嵌入功能测试
把登录逻辑封装成公共方法,在doActionAfterLogin_A.ts的before钩子中执行,无需单独拆分登录测试文件:
// doActionAfterLogin_A.ts import { remote } from 'webdriverio'; import { login } from '../utils/login'; // 封装的通用登录方法 describe('登录后功能A测试', () => { let driver: WebdriverIO.Browser; before(async () => { driver = await remote({ capabilities: { 'appium:noReset': false, // 每次测试都用全新安装 // 其他Cap配置 } }); await login(driver, 'testuser', 'password'); // 执行登录流程 }); after(async () => { await driver.deleteSession(); }); it('验证功能A', async () => { // 功能测试逻辑 }); });
这种方式保证了每个测试用例的独立性,无需切换noReset配置。
方案B:使用持久化登录状态
若需避免重复执行登录流程,可在登录测试完成后,通过Appium API修改应用的持久化存储保存登录状态,后续测试用例直接读取状态并跳过登录:
// login.ts中保存登录状态(Android示例) after(async () => { await driver.executeScript('mobile: shell', { command: 'am broadcast -a com.example.app.SAVE_LOGIN --es token "test-auth-token"' }); }); // doActionAfterLogin_A.ts中读取状态并跳转到目标页面 before(async () => { const token = await driver.executeScript('mobile: shell', { command: 'am broadcast -a com.example.app.GET_LOGIN' }); if (token) { // 直接跳转到登录后的主页 await driver.startActivity('com.example.app', '.MainActivity'); } else { // 执行登录流程 await login(driver); } });
内容的提问来源于stack exchange,提问作者Moo33

