Mock回放概述

各位,我们直接切入正题。Mock回放这个词,你要是第一次听到,可能会想:这跟Mockito那种模拟数据有啥区别?说白了,它是一种把真实交互录下来,再一模一样的回放的技术。我最早接触这东西是在做动态化UI的时候,组件状态太多,手动构造数据根本搞不过来,后来发现Mock回放简直就是救星。

什么是Mock回放

简单理解:录下来,再放一遍。

在Compose UI测试场景里,我们通常需要验证某个组件在收到一系列用户事件、异步回调、状态变化后,UI是否正确。传统做法是手动写一大堆 `state` 和 `event`,费时费力。Mock回放的做法是:先用真机或模拟器跑一次真实交互,把所有的输入事件、网络响应、状态变更全部录制成一段“磁带”;然后在测试环境中“播放”这段磁带,让组件以为它又经历了一遍同样的流程。

核心要素:

  • 记录器—— 捕获所有与UI相关的输入(点击、滑动、输入框文本)和输出(状态快照、渲染树)
  • 序列化格式—— 把记录的数据存为JSON或二进制文件,可版本控制
  • 回放引擎—— 在测试运行时,驱动Compose的 `state`、`LaunchedEffect`、`snapshotFlow` 等API按记录的顺序执行

为什么需要Mock回放

你想想看,一个复杂的Compose页面,可能包含几十个可组合项、网络请求、动画、键盘弹出、横竖屏切换……你要用传统单元测试覆盖每一种组合?我试过,光写一个页面的状态组合测试,就花了整整两周,最后还漏了一个边缘情况导致线上闪退。

Mock回放解决的核心痛点:

  1. 重复造轮子 —— 真实交互场景已经在开发过程中跑过了,何必再手动模拟一遍?
  2. 难以重现的bug —— 用户反馈某个操作后UI崩了,但测试环境死活复现不了。有了录制回放,直接让用户提供一次录制文件即可。
  3. 回归成本高 —— 每次改动UI,原有的手工测试用例可能作废。但Mock回放只要重新录一次,测试就自动更新。
  4. 协作效率 —— 产品、QA、开发共用同一份录制数据,减少沟通偏差。
问题 传统方式 Mock回放
测试编写时间 需手动模拟所有状态 一次录制,多次使用
维护成本 UI变化后手工重写测试 重录即可
覆盖完整交互 容易遗漏复杂流程 完全忠实于真实操作
协作偏差 各方理解可能不同 同一份数据说话

与传统测试的区别

这里我得说一句,Mock回放不是要取代传统测试,它是补充。我个人的习惯是:单元测试和集成测试继续用,Mock回放专门用来覆盖用户端到端的主流程和复杂交互。

关键差异点:

  • 数据来源:传统测试靠程序员手动构造输入;Mock回放靠真实操作录制
  • 确定性:传统测试每次运行结果理论上相同;Mock回放保证完全一致(只要录制和回放环境一致)
  • 维护方式:传统测试用例随UI变更手动调整;Mock回放只需要重新录制一次
  • 调试能力:传统测试定位问题需要加断点;Mock回放可以直接回放到出错的步骤,并打印出当时的所有状态

注意:Mock回放不适合所有场景。比如对时间精度要求极高的动画、随机数、传感器数据,录制回放可能产生偏差。我曾经遇到过因为设备屏幕尺寸不同,录制时的触摸坐标在回放时偏移导致误触,后来加了归一化坐标才解决。

// 一个简单的Mock回放使用示意(伪代码)
val recorder = UiRecorder()
recorder.startRecording(composableUnderTest)

// 用户在真机上点击了按钮
click(button)

// 录制结束,保存为文件
val replayData = recorder.stopRecording()
saveToFile("test_case_001.json")

// 在测试中回放
val player = UiPlayer()
player.load(replayData)
player.play() // 驱动Compose重现所有交互和状态变化
assertEquals(expectedState, actualState)

为了让你更直观的理解整个体系,我画了张图:

Mock回放核心流程 真实设备/模拟器 记录器 捕获事件/状态 序列化存储 JSON/二进制文件 版本控制 Git LFS / 云存储 回放引擎 驱动Compose state 断言 比较UI快照/状态 💡 录制和回放的环境需要尽量一致(屏幕尺寸、字体缩放、系统版本),否则可能出现偏差 我曾经在API 30上录制,在API 33上回放,结果Compose动画插值不同导致UI快照不匹配

回到开头的问题:为什么要用Mock回放?因为我在真实项目中吃过亏。有一次产品提了个需求,要在电商列表里加一个“滑动删除”的交互,我手写了十几个测试用例,结果上线后还是有滑出边界的情况。后来我用录制回放把真实用户的滑动轨迹录下来,直接作为测试数据,一次性找到了所有边界问题。从那以后,凡是涉及多点触控、嵌套滑动、手势冲突的测试,我都首选Mock回放。