移动应用测试自动化实践:一个斜杠程序员的血泪经验

杨思涵
2025-12-17 10:46
阅读 590

大家好,我是老K,白天在某中型互联网公司当“人肉API接口”,晚上接外包搞副业,顺便研究点云原生和K8s(虽然家里那台Mac Mini跑Minikube经常卡成PPT)。最近半年主要在折腾一个跨平台移动项目,前端用的是React Native,后端是Go+Node混搭——别问,问就是历史包袱。

上周五晚上11点,我正一边撸着猫一边在VSCode里狂敲代码(插件装了37个,启动慢得像老年机),产品经理突然在钉钉上@我:“老K,下周三上线,测试那边说回归测不过来,能不能搞个自动化?”

我内心OS:你早干嘛去了?双11压测的时候你不提,现在临上线三天说这个?

但没办法,谁让我是个靠口碑接外包的斜杠青年呢?今天这篇文章,就聊聊我在移动应用测试自动化这条路上踩过的坑、熬过的夜,以及最终怎么把这套流程跑起来的。


为啥非得搞自动化?因为手动测试真的会疯

我们这个项目是个电商类App,支持iOS和Android双端。每次迭代都有几十个页面改动,光登录、购物车、支付这三个核心路径,手动跑一遍就得半小时。测试同学小王已经连续加班两周,头发都快比我这个35岁老码农还少了。

更惨的是,上个月有一次上线,因为Android 12的权限弹窗样式变了,前端没适配,结果支付按钮被遮住,用户根本点不到!线上投诉炸锅,运维半夜把我电话打爆,我当时真的想砸电脑。

所以,自动化测试不是可选项,而是保命符——尤其当你面对多个机型、多个OS版本、还要兼顾用户体验和性能的时候。


技术选型:别整花活,稳定压倒一切

一开始我想直接上Appium,毕竟社区大、文档多。但试了两天发现,在真实项目里跑起来太脆了:

  • 元素定位经常失效(特别是React Native生成的View层级嵌套深得离谱)
  • Android模拟器启动慢得像树懒
  • iOS真机调试还得配证书,烦死了

后来一咬牙,改用 Detox + Jest 组合(专为React Native优化),配合 Firebase Test Lab 做真机云测。虽然学习曲线陡了点,但稳定性提升了一个数量级。

💡 小贴士:如果你不是RN项目,纯原生的话,iOS可以用XCTest + Fastlane,Android用Espresso + Gradle,但跨平台还是推荐Detox或Maestro(后者最近很火,但生态还不成熟)。


实战:从零搭建自动化流水线

第一步:本地跑通一个测试用例

先装依赖(我用的是Yarn):

yarn add detox jest --dev
npx detox init -r jest

然后配置 detox.config.js

// detox.config.js
module.exports = {
  testRunner: 'jest',
  runnerConfig: 'e2e/jest.config.js',
  apps: {
    'ios.debug': {
      type: 'ios.app',
      binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
      build: 'xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build'
    },
    'android.debug': {
      type: 'android.apk',
      binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
      build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug'
    }
  },
  devices: {
    simulator: {
      type: 'ios.simulator',
      device: { type: 'iPhone 14' }
    },
    emulator: {
      type: 'android.emulator',
      device: { avdName: 'Pixel_5_API_33' }
    }
  },
  configurations: {
    'ios.sim': {
      device: 'simulator',
      app: 'ios.debug'
    },
    'android.emu': {
      device: 'emulator',
      app: 'android.debug'
    }
  }
};

写个最简单的登录测试(e2e/login.test.js):

describe('Login flow', () => {
  beforeEach(async () => {
    await device.reloadReactNative(); // 热重载,省得重启App
  });

  it('should login successfully', async () => {
    // 等待首页加载完成
    await waitFor(element(by.id('welcome-screen')))
      .toBeVisible()
      .withTimeout(5000);

    // 点击“登录”按钮
    await element(by.id('login-button')).tap();

    // 输入账号密码
    await element(by.id('email-input')).typeText('test@example.com');
    await element(by.id('password-input')).typeText('123456');

    // 提交
    await element(by.id('submit-login')).tap();

    // 验证是否跳转到主页
    await waitFor(element(by.id('home-screen')))
      .toBeVisible()
      .withTimeout(10000);
  });
});

注意几个坑:

  • by.id() 必须在前端代码里显式加 testID 属性(React Native里是 testID="xxx"),否则找不到元素。
  • Android上 typeText 有时会失败,建议加 clearText() 先清空。
  • 真机测试时,iOS的Keychain可能缓存登录态,记得在 beforeEach 里清理。

第二步:搞定跨平台兼容性

不同平台的UI差异是最大痛点。比如:

平台 导航栏高度 弹窗动画 字体渲染
iOS 44pt 滑入 San Francisco
Android 56dp 淡入 Roboto

我们在前端组件库里统一加了 platform-specific 标记,并在测试中做条件判断:

const isIOS = device.getPlatform() === 'ios';

it('handles platform differences', async () => {
  if (isIOS) {
    await expect(element(by.id('ios-only-banner'))).toBeVisible();
  } else {
    await expect(element(by.id('android-fab-button'))).toBeVisible();
  }
});

另外,性能监控也得跟上。我们在每个关键路径后加了FPS和内存检查:

afterEach(async () => {
  const perf = await device.getPerformanceStats();
  console.log(`FPS: ${perf.fps}, Memory: ${perf.memory}`);
  expect(perf.fps).toBeGreaterThan(50); // 低于50帧就报警
});

第三步:集成到CI/CD,让自动化真正“自动”

我在家搭了个GitLab Runner(其实是Mac Mini兼职),配合Fastlane实现一键触发:

# Fastfile
lane :run_detox do
  sh("detox test -c ios.sim --headless")
  sh("detox test -c android.emu --headless")
end

但本地模拟器终究不够真实。于是我把关键用例扔到Firebase Test Lab跑真机:

gcloud firebase test android run \
  --type instrumentation \
  --app android/app/build/outputs/apk/debug/app-debug.apk \
  --test android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk \
  --device model=redfin,version=33,locale=en,orientation=portrait \
  --device model=iPhone14,version=16.0,locale=en

每周日凌晨2点自动跑全量回归,结果发到企业微信群。测试同学终于能睡个整觉了(虽然产品经理又开始提新需求了……)。


效果如何?数据说话

上线这套自动化后,我们:

  • 回归测试时间从 3小时 → 25分钟
  • 线上P0级Bug下降 70%
  • 发布节奏从 月更 → 双周更

最重要的是,我不用再半夜被叫起来修“按钮点不了”的问题了

当然,也不是没有代价:

  • 初期投入约 2周 搭建和调试
  • 需要前端配合加 testID(为此我还请前端小哥喝了三杯瑞幸)
  • 维护成本依然存在(每次大改UI都要同步更新测试脚本)

给同行的几点建议

  1. 别追求100%覆盖:核心路径(登录、支付、下单)必须覆盖,边缘功能可以手动。
  2. 前端和测试要坐一起:最好让前端自己写部分E2E测试,理解成本最低。
  3. 真机测试不能省:模拟器跑得再稳,也抵不过小米OV的真实碎片化生态。
  4. 监控比测试更重要:结合Sentry、Firebase Crashlytics做线上行为追踪,自动化只是第一道防线。

最后说句实在话:搞自动化不是为了炫技,而是为了让自己早点下班。毕竟,我还有两个外包项目等着交付,K8s集群也还没调优完呢!

如果你也在搞移动测试自动化,欢迎评论区交流(或者私信接外包,价格好说 😎)。

本文所有代码已在GitHub脱敏开源,搜索 “k-dev/mobile-test-demo” 即可。
P.S. 别忘了给你的VSCode装上 Detox Snippets 插件,少写80%样板代码!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝