Appium移动端自动化测试从入门到实战踩坑总结

轩辕冰冰 移动 阅读 761
赞 2 收藏
二维码
手机扫码查看
反馈

Appium环境搭建,别被官方文档绕晕了

搞移动端自动化测试,Appium确实是个不错的选择。不过第一次接触这玩意儿的时候,光是环境搭建就能让人头疼半天。我踩过不少坑,现在回头看看,其实没那么复杂。

Appium移动端自动化测试从入门到实战踩坑总结

首先说Android环境。如果你已经在开发Android应用,那基本环境已经有了。Android SDK、JDK这些必须的工具要装好,ANDROID_HOME环境变量也要配置到位。我建议直接用Android Studio,它会帮你处理大部分环境配置。

# 验证Android环境
adb devices

Node.js也是必需的,因为Appium是基于Node.js的。npm安装appium-server的话:

npm install -g appium
npm install -g appium-doctor

appium-doctor这个工具特别有用,能检查你的环境配置是否完整。跑一下命令就知道哪里有问题了。

连接设备这步最容易出错

设备连接看似简单,实际上很多人在这里卡住。USB调试开了吗?设备识别了吗?这些都是基本功。我遇到过几次设备连接不稳定的情况,后来发现是USB线质量不行导致的。

const { AppiumDriver, AndroidConfig } = require('nativescript-dev-appium');

// 基本配置
const capabilities = {
  platformName: "Android",
  deviceName: "your_device_name", // adb devices看到的设备名称
  appPackage: "com.yourapp.package",
  appActivity: "MainActivity",
  automationName: "UiAutomator2"
};

const driver = new AppiumDriver(capabilities);

这里要注意automationName的设置。Android建议用UiAutomator2,iOS用XCUITest。如果用错了,后面的元素定位会出现各种奇怪问题。

元素定位,XPath是最稳妥的选择

Appium的元素定位方式有很多,但我建议主要用XPath。虽然速度不如id或者class,但胜在稳定。特别是在复杂的界面中,XPath几乎能找到任何元素。

// XPath定位
const element1 = await driver.findElementByXPath("//android.widget.Button[@text='登录']");
await element1.click();

// 查找列表中的某个item
const listItem = await driver.findElementByXPath("//androidx.recyclerview.widget.RecyclerView/android.view.ViewGroup[2]/android.widget.TextView");

// 根据content-desc属性查找
const element2 = await driver.findElementByXPath("//android.widget.ImageView[@content-desc='返回按钮']");

这里有个坑:XPath在不同版本的Android上可能表现不一致。有些老版本对XPath支持不够完善,这时候可以结合resource-id或者text来定位。

TouchAction玩转手势操作

移动应用测试离不开手势操作。滑动、点击、长按这些都需要通过TouchAction来实现。我之前在一个电商项目的测试中,用这套方法测试商品列表的滑动加载功能。

// 滑动操作
const actions = driver.touchAction();
const size = await driver.getWindowSize();

// 向上滑动(刷新操作)
await actions
  .press({ x: size.width / 2, y: size.height * 0.8 })
  .wait(1000)
  .moveTo({ x: size.width / 2, y: size.height * 0.2 })
  .release()
  .perform();

// 长按操作
const element = await driver.findElementByXPath("//android.widget.ImageView");
await actions.longPress(element).wait(2000).release().perform();

这里需要注意坐标计算。我通常用屏幕尺寸的百分比来确定起点和终点,这样适配不同分辨率的设备就没问题了。

处理弹窗和权限请求

应用启动时经常会有各种权限申请弹窗,这些如果不处理会阻塞测试流程。我用的方法是在capabilities里加上相关的配置:

const capabilities = {
  platformName: "Android",
  deviceName: "your_device_name",
  appPackage: "com.yourapp.package",
  appActivity: "MainActivity",
  autoGrantPermissions: true, // 自动授权
  dontStopAppOnReset: true,   // 重启时不关闭app
  noReset: true,              // 不重置app状态
  automationName: "UiAutomator2"
};

如果还有弹窗处理不了,就得在代码里专门处理:

try {
  const permissionDialog = await driver.findElementByXPath("//android.widget.Button[@text='允许']");
  if (permissionDialog) {
    await permissionDialog.click();
  }
} catch (e) {
  console.log('没有找到权限弹窗');
}

这种处理方式虽然有点暴力,但确实管用。

等待策略,别让网络请求拖慢测试

Appium的等待机制很重要。移动端网络状况千变万化,数据加载时间也不固定。我之前犯过错误,用固定的sleep时间,结果测试变得很不稳定。

// 显式等待,推荐这种方式
const EC = protractor.ExpectedConditions;
await driver.wait(EC.presenceOf(element(by.id('some_id'))), 10000);

// 或者自己写等待函数
async function waitForElement(locator, timeout = 10000) {
  const startTime = Date.now();
  while (Date.now() - startTime < timeout) {
    try {
      const element = await driver.findElement(locator);
      if (element) return element;
    } catch (e) {
      await driver.sleep(500);
    }
  }
  throw new Error(Element not found within ${timeout}ms);
}

隐式等待和显式等待的区别一定要搞清楚。隐式等待是全局的,显式等待是针对特定元素的。

截图和日志,排错的好帮手

自动化测试跑着跑着就挂了,这是常有的事。截图功能在这种时候特别有用。每次失败都保存一张截图,能快速定位问题。

// 截图并保存
async function takeScreenshot(driver, filename) {
  const screenshot = await driver.takeScreenshot();
  const fs = require('fs');
  fs.writeFileSync(./screenshots/${filename}.png, screenshot, 'base64');
}

// 在关键步骤加截图
await takeScreenshot(driver, step_${Date.now()});

日志记录也很重要。把关键的操作步骤和结果都记下来,方便后期分析测试报告。

并行测试,提升效率的关键

一个一个跑测试用例太慢了,得想办法并行执行。我的做法是给不同的设备分配不同的端口,然后同时跑多个实例。

// Appium server启动时指定端口
// appium -p 4723 -bp 4724
// appium -p 4725 -bp 4726
const capabilitiesDevice1 = {
  port: 4723,
  udid: 'device1_udid',
  // 其他配置...
};

const capabilitiesDevice2 = {
  port: 4725, 
  udid: 'device2_udid',
  // 其他配置...
};

这样可以在不同设备上同时跑测试,效率提升明显。不过要注意设备资源竞争的问题。

踩坑提醒:这三点一定注意

首先,apk签名问题。不同环境下打包的apk签名可能不一致,导致安装失败。解决办法是用–no-sign选项跳过签名验证,或者保持构建环境一致。

其次,页面加载时机。有时候页面看起来加载完了,但实际上数据还没完全渲染。这时候去找元素就会找不到。建议根据关键元素的出现来判断页面是否真正可用。

最后,设备兼容性。不同品牌不同版本的Android ROM差异很大,特别是MIUI、EMUI这些定制系统。最好准备几台主流品牌的测试机,覆盖常见的使用场景。

以上是我用Appium做移动端自动化的一些实践经验,主要是踩坑后的总结。这个工具的拓展用法还有很多,后续会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论