移动应用测试自动化?我摸鱼时顺手搞定了

极客小岛
2026-01-03 12:10
阅读 748

上周五晚上,我正刷着 B 站“躺平哲学”视频,突然钉钉弹出一条消息:“小张,下周一上线前,测试覆盖率必须提到 80%。”——产品经理的语气像极了催命符。
我叹了口气,关掉 VSCode 里刚写了一半的 Spring Boot 微服务 demo(别问,问就是分布式系统兴趣使然),默默打开了公司那套祖传的移动端项目。

入职新公司才两个月,团队氛围倒是挺佛系,但上线节奏一点不含糊。我们做的是一个面向 C 端用户的 App,前端用 React Native 写的,后端是 Spring Boot + 区块链存证模块(对,你没看错,为了“可信”,硬塞了个轻量级联盟链)。问题是:每次改个按钮颜色,测试同学都要手动跑十几台真机+模拟器,效率低到让人想原地退休。

于是我想:与其等别人救我,不如自己动手,丰衣足食。反正摸鱼也是摸,不如摸点有用的——搞一套移动应用测试自动化流程。


自动化?听起来很卷,其实也没那么可怕

很多人一听“测试自动化”,脑海里立刻浮现 CI/CD、Selenium Grid、Appium 集群……但对我们这种中小团队来说,能跑通就行,先别追求完美。我的目标很简单:

让 UI 测试脚本在本地一键运行,不依赖测试同学手动点点点。

我翻了翻公司代码仓库,发现前端同事已经用 Jest + React Testing Library 写了一些单元测试,但端到端(E2E)测试几乎是空白。更离谱的是,区块链那部分接口居然只靠 Postman 手动验证——这要是线上出问题,怕不是要背锅到明年。

于是我决定从三方面入手:

  1. 前端 UI 自动化:用 Appium + WebDriverIO 覆盖核心路径(比如登录、提交表单)
  2. 后端接口验证:用 Spring Boot Test 写集成测试,顺便 mock 区块链节点
  3. 跨端兼容性:确保 iOS 和 Android 表现一致(别问我为什么,上次因为安卓字体渲染偏移 2px 被用户骂上应用商店)

Appium 搞起来,但别被坑死

说实话,第一次配 Appium 的时候,我差点卸载 VSCode。
adb devices not foundXcode signing failediOS simulator stuck on boot……这些问题轮番上阵,堪比打 Boss。好在我装了一堆插件(感谢 Remote - WSL、Appium Desktop、ESLint + Prettier 组合拳),总算把环境搭起来了。

关键配置其实就几行 wdio.conf.js

// wdio.conf.js
exports.config = {
  runner: 'local',
  specs: ['./test/specs/**/*.js'],
  capabilities: [{
    platformName: 'Android',
    'appium:deviceName': 'Pixel_4_API_30',
    'appium:platformVersion': '11.0',
    'appium:automationName': 'UiAutomator2',
    'appium:app': path.join(process.cwd(), 'android/app/build/outputs/apk/debug/app-debug.apk')
  }, {
    platformName: 'iOS',
    'appium:deviceName': 'iPhone 14',
    'appium:platformVersion': '16.2',
    'appium:automationName': 'XCUITest',
    'appium:app': path.join(process.cwd(), 'ios/build/Build/Products/Debug-iphonesimulator/MyApp.app')
  }],
  // ...其他配置
};

注意:Android 路径和 iOS 路径完全不同,而且 iOS 必须用 .app 文件(不是 .ipa!),还得提前用 Xcode 编译过。我踩过的坑:直接拿 release 包跑,结果签名不匹配,Simulator 直接闪退。

测试脚本写起来倒挺爽,比如登录流程:

// test/specs/login.spec.js
describe('Login flow', () => {
  it('should login successfully with valid credentials', async () => {
    await $('~emailInput').setValue('user@test.com');
    await $('~passwordInput').setValue('123456');
    await $('~loginButton').click();

    // 等待首页加载(带超时)
    await expect($('~homeScreenTitle')).toBeDisplayed({ timeout: 10000 });

    // 断言:检查是否跳转成功
    const title = await $('~homeScreenTitle').getText();
    expect(title).toEqual('Welcome!');
  });
});

这里用了 ~ 前缀,代表 accessibility ID —— 这是跨平台最稳的方式,比 XPath 或 class name 可靠多了。前端同学只要在 RN 组件里加个 testID="loginButton" 就行,几乎零成本。


后端别拖后腿:Spring Boot + 区块链怎么测?

我们的 Spring Boot 服务有个 /api/submit 接口,会把用户提交的数据哈希后写入 Hyperledger Fabric 联盟链。但链节点不能随便 mock 啊?总不能每次测试都连真实网络吧?

灵机一动:用 @MockBean 把区块链客户端干掉!

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {
    "blockchain.enabled=false" // 关键!测试时禁用真实调用
})
class SubmissionControllerTest {

    @Autowired
    TestRestTemplate restTemplate;

    @MockBean
    private BlockchainService blockchainService; // 模拟区块链服务

    @Test
    void shouldReturnSuccessWhenDataSubmitted() {
        // 模拟区块链返回成功
        when(blockchainService.write(anyString())).thenReturn("tx-12345");

        ResponseEntity<String> response = restTemplate.postForEntity(
            "/api/submit", 
            new SubmissionRequest("Hello World"), 
            String.class
        );

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).contains("tx-12345");
    }
}

这样,测试既快又安全,还不污染真实链数据。上线前再开 blockchain.enabled=true 跑一遍冒烟测试就行。


真机 vs 模拟器:别信“看起来一样”

有次我本地跑 Appium 测试全绿,结果 QA 说“安卓真机上按钮点不动”。查了半天才发现:某些低端机屏幕 DPI 不同,元素坐标偏移,导致点击失效

解决方案?两点:

  1. 优先用 accessibility ID 定位(再次强调!)
  2. 加个 retry 机制
const clickWithRetry = async (element, maxRetries = 3) => {
  for (let i = 0; i < maxRetries; i++) {
    try {
      await element.click();
      return;
    } catch (e) {
      console.warn(`Click failed, retry ${i + 1}/${maxRetries}`);
      await browser.pause(1000);
    }
  }
  throw new Error('Element click failed after retries');
};

另外,iOS 和 Android 的交互逻辑天然不同。比如 iOS 的 alert 弹窗要用 acceptAlert(),而 Android 可能是普通按钮。写测试时得分开处理,别幻想“一套脚本通吃”。


成果如何?摸鱼也有生产力

折腾两周后,我把 E2E 测试集成进了 GitLab CI:

# .gitlab-ci.yml
e2e_test:
  stage: test
  script:
    - npm run build:android
    - npm run build:ios
    - npm run test:e2e
  only:
    - merge_requests

现在每次提 MR,流水线自动跑核心路径测试。上线前再也不用手动求测试同学加班了,他们甚至给我点了杯奶茶表示感谢(虽然可能是客套)。

最关键的是——我的摸鱼时间反而变多了。因为自动化挡住了 70% 的回归 Bug,剩下的问题基本是业务逻辑漏洞,而不是“点不到按钮”这种低级错误。


说点实在的:自动化不是银弹

别被网上那些“全自动无人值守”的宣传骗了。现实是:

项目 真实情况
维护成本 脚本经常因 UI 改动而失效,需持续更新
覆盖率 核心路径可覆盖,边缘场景仍需人工
速度 一次完整 E2E 测试约 8 分钟,不适合 TDD
ROI 对频繁迭代的 App 极高,对静态页面意义不大

所以我的建议是:先自动化最关键的 3 条路径(比如注册、下单、支付),跑通后再扩展。别一上来就想覆盖 100% 场景,那只会把自己累死。


最后:技术人,既要躺平,也要有底线

写这篇文章时,我刚修完一个因时区问题导致的日期显示 Bug(UTC vs 本地时间,经典陷阱)。虽然日常佛系,但该搞定的事情,还是要搞定。

毕竟,摸鱼摸的是心态,不是责任
用自动化把重复劳动干掉,剩下的时间,才能真正去研究分布式系统、区块链底层,或者——继续刷 B 站。

对了,如果你也在搞移动测试自动化,欢迎交流。
不过别在周五晚上找我,那时候我在躺平。

评论 0

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