react-native-vision-camera 自动化测试避坑指南:搭一条真能测到相机的 E2E 流水线
【免费下载链接】react-native-vision-camera📸 A powerful, high-performance React Native Camera library.项目地址: https://gitcode.com/GitHub_Trending/re/react-native-vision-camera
刚补完 react-native-vision-camera 自动化测试,聊聊真实的踩坑过程。相机库测试最容易死的地方是:Jest 全绿,真机却不行。因为你 mock 得越全,相机就越"存在",断言只证明 mock 忠实,不证明相机工作。
写测试前先翻车三次
不先讲分层理论,先看事故。
"mock 到全绿"型:在 node 环境里把原生桥全 mock 掉,session 的启动停止断言一路通过,真机上configure却卡死。那个测试从头到尾没碰过硬件,什么回归都抓不到。
"流水线沉默"型:CI 任务挂四十分钟,模拟器没起来,测试日志一个字没有。根因是 KVM 权限没开,机器在纯软渲染,卡死在开机阶段。
"权限失忆"型:adb install -r重装 APK 后,每个用例的权限断言集体变红。pm grant授予的权限不会随重装自动恢复,重装就得重授。
三次翻车指向同一个结论:相机库的测试只有两种,纯函数和真机。前者哪都能跑,后者必须落在硬件上,没有第三种。
先看 CI 全貌:你的代码会过哪几个节点
别急,先倒过来看流水线。项目有两条真机测试工作流:.github/workflows/harness-android-emulator.yml跑在模拟器上,尽力而为,硬件相关用例可能跳过;.github/workflows/harness-aws-device.yml跑在 AWS Device Farm 的真手机上,结果才是 source of truth。两者都在apps/simple-camera/**或packages/变动时触发。
模拟器链路做了不少缓存:先构建 APK,Gradle、CMake 缓存都有,APK 本身也进缓存;再用 KVM 起模拟器。模拟器启动参数里带了虚拟前后摄像头,所以预览至少能亮起来。
跑测阶段交给apps/simple-camera/scripts/run-harness-android-ci.sh:等设备上线、装 APK、验证进程没秒崩,最后用硬超时包一层跑测试,超时就失败,绝不无限等。
3 分钟跑通第一条真机用例 📱
git clone https://gitcode.com/GitHub_Trending/re/react-native-vision-camera cd react-native-vision-camera && bun i测试入口在示例应用里,runner 是 react-native-harness:它把一个 Jest 兼容的 runner 嵌进 App,经 Metro 驱动,本质是"真实 App + 测试桥",不是测试框架加模拟器的组合。设备与超时配置在apps/simple-camera/rn-harness.config.mjs。
连上真机后这样跑:
adb shell pm grant <BUNDLE_ID> android.permission.CAMERA bun run test:harness:android -- --testPathPatterns=photo第一行授相机权限,重装 APK 后记得重授。第二行只跑拍照相关文件,厂商和型号参数可从adb shell getprop里查。用例文件由jest.harness.config.mjs按__tests__/**/*.harness.ts匹配,命名别乱改。
怎么让测试不活在 mock 里
其实这里的分层比一般项目简单。
| 层级 | 覆盖什么 | 位置 |
|---|---|---|
| 纯函数 | getUIRotation等朝向计算、坐标换算 | apps/simple-camera/__tests__/,.harness.ts后缀 |
| 真机 E2E | session、拍照、录像、帧输出、多输出 | 同目录,按领域分文件,一个领域一个文件 |
| 文档站 | 页面渲染、API 参考页布局 | docs/tests/,Playwright 快照对比 |
写法规范都在apps/simple-camera/__tests__/README.md里,反直觉的一条是:禁止抽共享 helper。每个it块都从零完整建一个 session,测试代码和用户代码一模一样。这样有人报 bug 时,直接写一个最小失败用例提 PR,CI 的红色运行本身就是复现。
断言原则是只断语义结果:拍出的照片width > 0,未configure就start()必须抛错,监听器按序触发。typeof x === 'number'这类断言是噪音,类型不对桥早就抛了。
"设备不支持"和"真 bug"怎么分
相机设备之间能力不齐,skip 纪律最容易在这里破功。
it('resolves HDR when supported', async (context) => { if (!backDevice.supportsPhotoHDR) { return context.skip('photoHDR: not supported on this device') } // 从这里开始硬断言 })硬要求直接expect,挂掉就是 bug;软能力先用device.supportsPhotoHDR这类能力位查询,不支持就context.skip带原因,skip 会进 JUnit 报告。别用 try/catch 静默吞错,也别用sleep(500)等相机"缓过来",等真实生命周期事件。
让覆盖率报告不再骗人
说白了,真机测试的覆盖率不看行数。流水线里没有单一覆盖率百分比,而是根package.json里的jest-junit产出 JUnit XML,从工作流下载 artifact 查看,failed、skipped、passed 分列清楚。
拿到报告看两件事:哪个it挂了,哪些软需求被 skip 以及为什么。skip 原因才是真实的能力缺口清单。文档站的 Playwright 快照则拿仓库里已提交的 PNG 做基线比对。
流水线红了从哪查 🔴
- 先下载失败运行里的
harness output logartifact,每个用例的 JS 控制台输出都在里面。 - 翻 JUnit 的 skip 摘要,skipped 说明设备缺能力,不是 bug。
- 模拟器和真机结果不一致,以真机为准。Device Farm 的摄像头常被胶带贴住,预览带灰色噪点是正常现象。
- 超时类失败,检查工作流里模拟器启动超时和测试硬超时是否配紧;CI 下
bridgeTimeout和maxAppRestarts都会放宽。
相机库测试拼的不是 mock 数量,是让测试碰到真硬件的比例。先把utils里的纯函数用例跑绿,再接一台真机把拍照全流程走通,两者都稳了,再让 CI 在两条链路上各跑一遍。
apps/simple-camera/tests/README.md apps/simple-camera/rn-harness.config.mjs
【免费下载链接】react-native-vision-camera📸 A powerful, high-performance React Native Camera library.项目地址: https://gitcode.com/GitHub_Trending/re/react-native-vision-camera
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考