刚刚培训结束,培训的不少时间是用来看各种乱七八糟的书籍。其中一个重要的领域就是有关测试的部分。这里整理一下前一阵子的笔记。
首先读了一本非常经典的书籍:tdd by example 也是测试驱动开发的由来了。不是第一次读这本书,但是这次读了之后依然有新的体会。读的时候就觉得书中的第一部分的小步都太小了,很多实现都是显而易见的。然而讽刺的是我自己在走这个例子的时候却因为步子太大而忘记在构造函数中设置 MoneyType 反而拖慢了进度。很小的步子隐含了按着步骤走可以大大减少中间出错而打乱节奏甚至是需要 debug 的风险,而debug 所花费的时间与小步走所花费的时间相比绝不是一个数量级的。在练习的过程中细细的去想为什么要这么写以及为什么是这么一个重构的流程引发了我找到了一些其他的书籍逐渐弄明白了书中很多忽略了的细节。比如第一部分中对 expression 钱包的隐喻其实并不是 tdd 所带来的好处,而是因为作者本身有这个设计思维,所以 tdd 不是所带来的益处。
另一个被很多人忽略的点在于每次在写一个测试之前是有一个 task 列表的。tdd 要求你每次写测试之前都知道自己想要实现的功能是什么,而让这个测试的通过就代表某一个小的功能的完成。列出系统想要什么样子并且可以有一个测试对应,这就是在做设计了,这就是 tdd 带来的主要好处吧~可是做不出来设计怎么办?那就是 ooad 的能力问题了。在有了测试之后就用最直接最简单的办法去实现,实现的方式有三种:
实现之后 tdd 讲要做重构。这里的重构和 重构 那本书里面讲的很多是一致的。但是我之前在对重构的粒度和范围的理解确实不到位。通过各种形式的消除重复(代码重复、数据重复)把一个 fake 的实现逐渐转化为一个实际的方案才是重构的核心。
所以 tdd 是对重构的能力有很高的要求的。在之后的读书过程中逐渐的发现,重构是一个非常强大的技术,它不仅仅是在技术层面,在领域层面也是一个很好的工具。仔细想想一个复杂的,需要多年运行和维护的软件如何才能保证代码与架构不腐化堕落呢?唯有坚持重构了吧。
这本书其实主要讲的是单元测试而不是测试驱动开发。开篇它提到了 tdd 所需要的三种核心技能:
这本书还明确的定义了什么是单元测试:不是那种一个函数对应一个测试,而是对一个最小工作单元的测试。小巨人的培训里也是强调每个测试是完成了某个功能,这个测试就是这个功能的文档。它定义了一些好的单元测试的特性:
它还强调了单元测试与集成测试的区别:集成测试结果不稳定、速度缓慢、多个真实依赖。比如依赖数据库、依赖文件系统。过多的依赖导致的是不能明确的指导什么导致了测试的失败。这也让我开始反思自己以前写的很多与数据库相关测试了。
还有它提出了优秀测试的支柱:
这本书的思想正如标题所示,通过测试驱动建立面向对象的软件。它认为好的设计是可以通过测试一步步驱动出来的。但是它依然强调事先要知道一些基本的面向对象设计原则,这也是大家的共识吧。
书中提到了要将验收测试和单元测试结合起来,通过大循环套小循环来完成测试。
书中提到了通过 mock 和 stub 来隔离各个对象之间的依赖,这个其他几本书中也有所提及。
然后提到了一个对象出现的几个方式:
在 TDD 的时候 breaking out 和 budding off 的动作会比较多。
这本书有点像是一个 cookbook。很多思想其他的书中都讲到了,我也没有留下非常深的印象。在有关 web 测试与数据库测试方面有一些值得记录的地方。
对于 mvc 来说 m 是不涉及任何 web 框架的,所以不用提。但是 c v 就是具体问题具体分析了。其中 c 的测试主要是依靠 request 与 response 的 mock。但是结合我目前前后端分离,后端都是 rest api 的情况来说更多的是采用类似于集成测试的方式。那什么叫类似呢?
然后在测试 controller 的时候 view 就随着一起测试了。毕竟都是 json 而已。
首先说明了 db 的 tdd 用 unit test 是没有意义的,因为和数据库如此强依赖,把数据库 mock 掉感觉就没再测什么了。但是,如果每次测试都启动一个 mysql 也是听蛋疼的。所以这里人家采用了类似 fake 的方式:引入一个 memory database。这个是我应该学习的。之前都是直接连接 mysql 这也导致在 docker 里面启动 mysql 的情况。
在 teardown 做清理,采用 tx.rollback()