My Blog.

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调用。
  • 集成测试
  • 增加埋点