我并不试图系统地谈TDD,因为我在这方面的经验还不足以做到这一点。但是TDD的一些方法论和工具确实解决了我在实际开发过程中遇到的一些问题。
第一个问题是健壮性。尤其是对于我这样思维发散、容易迷失又喜欢重构的人,TDD给了我一个明确的目标,在一切刚开始的时候,我要做的只是满足测试用例,让它们由红变绿。到了重构阶段,我就可以把精力放到优化代码上面,且不必对健壮性有过多的担心。在此之前,由于手动测试比较麻烦,很多时候惰性会说服潜意识告诉自己这部分逻辑很简单、不会有什么bug,结果提交测试后却一再地发生问题。
第二个问题是效率。曾经只有不忙的时候才会写测试用例,但是现在一般都会写,因为确实可以提高开发效率,而不只是保证重构的健壮性。可能对于所有对单元测试不熟悉的人来说,这都是匪夷所思的,我也曾认为单元测试只是用来保证健壮性的,必然和开发效率是一对矛盾,因为要花很多时间写测试代码。这种想法首先是因为对单元测试不熟悉,所以才需要花很多时间在上面。其次是没有看到它所带来的好处。
例如你要实现一整套退款单的功能,退款单是依赖订单的,这意味著你需要不只一条符合条件的订单数据。但是订单是有状态的,随著时间的推移、人为的操作或者开发过程中程序的影响,状态会变得不再符合你的要求,这时候你就必须停下来去做更多符合要求的订单。而创建订单也存在很多麻烦,你需要找到有库存的商品,有时候还要去库管系统中补充库存,然后下单并支付。最后花了很多时间做出来的订单,因为正在开发的退款单的程序出现的一个小问题变得不能使用!时间被大量浪费在创建所依赖的上游数据上。
这时候如果用单元测试并mock出依赖关系,就可以随时随地、无限制地执行相关的业务逻辑。对效率的提高不言而喻。
特别的,mock并不是随时可以抓起的救命稻草,它的原罪在于:不负责任地滥用mock会导致很多问题被掩盖。因此,对mock的使用应该是节制的、目标明确的。
当然,这并不包括单纯地为了提高代码覆盖率而使用mock,只所以这是个在很多强制TDD的团队里的普遍现象,归根结底是因为KPI是万恶之源,任何好的方法论,一旦沦为KPI,就离死不远了。