Flutter 单测问题
从 Unit Test、Widget Test 到 Integration Test,梳理 Flutter 测试分层、平台能力边界以及测试与代码设计的关系。
Flutter 单测可能包含的移动端特性
核心区别在于面向的对象不同。
后端单测主要验证:
数据和业务逻辑是否符合预期。
模型:
输入
↓
函数 / Service
↓
输出
例如:
输入订单金额和用户等级:
100元
VIP用户
↓
计算优惠
↓
80元
测试关注的是计算逻辑是否正确。
而前端单测除了验证逻辑,还需要验证:
状态变化之后,界面是否符合预期。
模型:
用户操作
↓
状态变化
↓
重新渲染
↓
界面变化
比如:
Given:
用户未登录
When:
点击收藏按钮
Then:
显示登录弹窗
这个过程不是直接调用某个状态修改函数,而是模拟真实用户行为:
用户点击
↓
组件响应
↓
状态变化
↓
重新build
↓
检查UI结果
类似于把组件放进一个假的运行环境中,模拟点击、输入、滑动等行为。
这一部分严格来说已经从 Unit Test 逐渐进入了 Component Test / Widget Test 的范围。
不过实际开发交流中,很多人会比较宽泛地把:
- 不启动完整App
- 不依赖真实后端
- 执行速度较快
的测试统称为“单测”。
这并不是非常严谨,但工程实践中这种术语混用很常见。毕竟软件工程里的很多名词最后都会经历一个阶段:定义写在文档里,实际交流靠猜。
Flutter测试和移动端特性的关系
Flutter 的普通 Unit Test 和 Widget Test 主要运行 Dart / Flutter Framework 部分。
它们不会真正加载:
- iOS Native代码
- Android Native代码
- 系统权限弹窗
- 系统级组件
所以涉及平台能力时,例如:
- 相册选择
- 相机调用
- 文件权限
- 推送
- 生物识别
通常需要 Mock Platform Channel。
例如:
Flutter代码
↓
MethodChannel
↓
iOS / Android 原生实现
普通测试环境:
Flutter代码
↓
Mock MethodChannel
↓
返回模拟结果
所以:
你可以测试:
点击上传按钮
↓
调用照片选择逻辑
↓
拿到图片数据
↓
进入上传流程
但是无法只靠普通单测证明:
iOS照片选择器是否真的能打开
Android权限弹窗是否正常显示
这些需要更高层级的测试,例如 Integration Test。
单测是什么
单测的核心目的:
当未来有人修改这段代码时,能不能快速知道有没有破坏已有能力。
它对应的是更高层级的:
- 集成测试
- 端到端测试
单测最大的价值不是证明“现在没有Bug”,而是给未来修改提供安全感。
例如:
今天:
calculatePrice()
运行正常。
半年后:
别人重构代码:
calculatePrice()
如果没有测试:
可能上线后才发现价格计算错误。
如果有测试:
flutter test
↓
失败
↓
发现重构破坏已有逻辑
所以单测可以给系统重构提供信心。
单测的心智模型
你的代码
↓
测试框架启动
↓
调用你的函数 / 组件
↓
获得实际结果
↓
expect比较预期结果
↓
通过 / 失败
例如:
final result = calculatePrice(100);
expect(result, 80);
本质就是:
拿实际运行结果和预期结果比较。
单测和代码设计的关系
单测还有一个额外价值:
它可以帮助发现代码设计问题。
如果一段代码:
- 依赖很多外部服务
- 包含很多职责
- 状态变化复杂
那么测试通常也会变得困难。
例如:
一个函数同时:
调用API
↓
修改数据库
↓
更新缓存
↓
发送通知
↓
修改UI
测试时需要模拟大量环境。
这往往说明代码职责没有拆分。
所以:
单测难写,很多时候意味着代码设计存在耦合问题。
单测是否有必要
对于初期开发:
不一定必须马上写大量单测。
原因:
- 功能变化快
- 代码可能快速推翻
- 人工测试成本较低
这个阶段:
开发速度可能比测试覆盖更重要。
但是对于:
- 大型项目
- 长期维护项目
- 多人协作项目
测试价值会越来越高。
一种常见实践是:
把测试逐渐融入开发流程,例如 TDD。
TDD(Test Driven Development)
经典流程:
先写测试
↓
实现功能
↓
重构代码
也就是:
先定义希望代码满足什么行为,再写实现。
不过实际工程中,不一定所有功能都严格TDD。
很多团队更常见:
开发功能
↓
补充测试
↓
持续维护
尤其探索阶段产品,需求变化非常快,提前给所有代码写完整测试,有时候会出现“代码还没确定,测试已经写了一座城堡”的情况。
Flutter测试分层理解
可以简单理解:
Integration Test
|
--------------------------------
| |
Widget Test Native验证
|
Unit Test
Unit Test
验证:
- 数据转换
- 状态逻辑
- 算法
- Service
Widget Test
验证:
- 用户操作
- 状态变化
- UI展示
Integration Test
验证:
- 真机环境
- 权限
- 系统能力
- 完整业务流程
最终总结:
后端单测主要验证“输入经过逻辑是否得到正确输出”。
前端测试需要额外验证“状态变化是否产生正确UI”。
Flutter因为连接移动端系统能力,所以除了测试Dart逻辑和Widget,还需要通过Integration Test验证真实设备环境。