no-auto-test-dev-philosophy
灵活 敏捷 迭代。
自动化测试 辩思
测试必不可少
想想看没有充分测试的代码, 哪一次是一次过的? 哪一次不需要经历下测试的鞭挞? 不要以为软件代码容易改, 就对于质量不切实际的自信---那是自大!
不适用自动化测试的case
- 遗留系统。
- 太多的依赖方, 不想用过多的mock => 逻辑核心能力就是那些不容易测试的
- 大量依赖 第三方商业服务、自己的rpc服务/http服务
- IO 操作密集的
- 似乎很少有IO密集型的应用具备完善测试的
- (注意) 容易测试的一般是 无状态的(无IO状态的), 就内存中存储了状态
- 对应的代码在线上的时间比较短, 属于实验性代码。
- 越是早期, 自动化测试的必要性就越弱。靠手工测试就可以支持一定水平的质量。
无自动化测试系统设计方法论
这一切都需要 一点代价, 但是相对于 单元测试来说, 代价还是轻微很多。
无自动化测试, 也要关注可测试
- 模块、组件间的边界情况
- 使用 "谦虚对象" 设计模式来隔离可测试和不可测试的领域, 划定边界。
划分边界 注意耦合关系
需要更审慎的对对应的模块做足够的前置思考:
- 重写的风险增大了 必不可少, 重构的能力了大大降低了!
常见的分层边界有 GUI层、 逻辑层 、网络层..
- 如果逻辑层代码比较复杂, 那么可以将逻辑层代码单独摘离出来进行测试
- GUI 层可以拆分 容易测试的部分和不容易测试的部分> by "谦虚对象"
模块测试
落地到具体的测试行为上, 还是按模块测试比较好。
使用测试api
更多参考: 架构敏捷之道 P220
在自动化测试工作流里: 如果测试架构设计不好、测试代码和生产代码耦合过高的话, 可能出现脆弱
的测试。修改一个api需要修改大量的测试代码。
如果使用无自动化支持, 那么测试API必不可少。类似于一个超管API,能够跳过各种权限验证、逻辑验证, 直达核心逻辑。
- 本质上是通过修改状态来加速测试流程。
用 测试API 和 合理的mock能力 来更灵活的撬动 软件质量
- 对于IO密集型应用 需要的测试API能力:
- clear : 清除 所有的缓存影响
- remove : 删除指定影响、删除指定字段
- set(指定值) : 给指定值设置状态
- get(查询): 查询指定值
- 环境隔离:
- 测试API甚至需要运行时隔离 乃至 编译器隔离,避免污染线上环境。
简短 手动测试 反馈时间
哪怕是手动测试, 也需要注意测试反馈时间。
影响程序员的效率的两大因素: 调试和验证反馈时长。
- 足够的测试大大减少调试的次数
- 验证反馈时长缩短, 每个小迭代反馈都更容易、效率更高。
必要情况下 使用自动化测试
自动化测试有代价, 对于不爱写测试的同学需要重视测试, 对于狂热的TDD爱好者, 也要警醒测试代码也是需要花费人力的。
对于经常出现问题的、非IO密集型的逻辑, 可以使用自动化
测试来防御之后可能导致的问题。
- 频繁出现问题的区域, 使用自动化手段防御 ROI很高
- IO密集型的逻辑, 本质上复杂性和风险都在依赖的IO组件方---无论是文件还是数据库, 都是更为稳定、健壮的代码。
覆盖率不一定要很高, 但是验收测试要通过。一般来说冒烟测试是最基本的。
让表现结果 明显 可视化
- 错误的路径 异常日志打出来,
- 正确的路径 可以日志 绿色高亮
- 可以使用 侵入式的 Assert 临时代码
- 更为认真的撰写日志, 能够容易的阅读出 对应的流程
- 日志可以关注下 关键字, 和 验收注释 里面的关键字对应
- 可以用 关键字起头 => 扩展下: 越明显的单词越先出现
框架? 和框架耦合的代码要薄薄的
如果和框架过度耦合的代码很多, 那么测试就必须依赖框架的行为。
这时候想要就某个模块进行测试就比较难。例如 高度使用springboot框架的 @resource
编码工作步骤
- 主要逻辑 用 日志填充。
- 逐步更换为 具体的 IO调用。
- 集成测试
- 增加埋点