论坛首页 综合技术论坛

什么是“测试驱动开发”

浏览 71487 次
该帖已经被评为精华帖
作者 正文
   发表时间:2006-04-24  
经过这几天的观察,我相当surprise地发现,很多同志还没有明白什么是“测试驱动开发”:开发之前写的测试是干什么用的——之所以说“surprise”,因为既有“惊讶”,也有“惊喜”。结合着最近做高校巡讲遇到的问题,我稍微做一些解释。

(前面的讨论:
http://forum.iteye.com/viewtopic.php?t=19959
http://forum.iteye.com/viewtopic.php?t=20035

看“测试驱动开发”这个名字,首先应该明确:与它对应的是“文档驱动开发”。它是一种开发过程,这里的测试是一个设计问题,而不是QA问题。在没有TDD之前,“正统的”开发过程要求有设计文档:高层设计描述一个模块“做什么事”,详细设计描述一个模块“如何做这些事”。软件工程课讲得清楚,只有源代码的软件不能算软件,因为它不可理解、不可维护;源代码加上文档,才算是程序员完整地交付了自己的工作。要做任何一件事之前,你必须首先清楚地知道自己要做什么(以及不要做什么),否则那就是crack,不是在从事职业的软件开发。

但这种文档有几个致命的缺陷。第一,自然语言的描述容易产生歧义;第二,不能自动化地验证;第三,不能保证文档与程序同步。测试驱动开发正是为了解决这些问题而产生的。在编写一段代码之前你所写的测试,不是为了确保这段代码不出错,而是为了描述你想要做的事情。当你拥有这个测试之后,持续集成会始终确保你的代码恰好是做了你想要做的事情。测试驱动开发是一种设计方法,它清晰无二义地描述你的设计,并保证设计与实现一致。

所以几个典型的问题就可以很清楚地找到答案。单元测试要不要是白盒的?要。因为你是在描述如何实现这个模块,而不是验证它的输入输出正确性。单元测试要不要把每个模块放到真实的事务或者并发环境下测试?不要。因为你只是描述当前模块的实现,真实环境下的正确性由集成测试和QA来保证。为什么模块内部实现的变化要同时导致测试变化?因为你的思路、你的设计变化了,你应该有一个文档描述这件事情。
   发表时间:2006-04-24  
重构一个组件时,是
先按照新的设计修改对应的单元测试,然后修改组件的实现使其通过所有(包括那个新的)测试,
还是
直接修改组件的实现,如果新的实现能通过所有测试,重构成功,否则宣布重构失败,继续修改?

或者说以上两种做法哪个才是真正的重构?
0 请登录后投票
   发表时间:2006-04-24  
TDD是修改功能代码之前,先修改测试来表达你的实现方式,然后运行测试,fail,再修改功能代码,再运行测试,直到测试通过,则证明你的修改完成。
0 请登录后投票
   发表时间:2006-04-24  
daquan198163 写道
重构一个组件时,是
先按照新的设计修改对应的单元测试,然后修改组件的实现使其通过所有(包括那个新的)测试,
还是
直接修改组件的实现,如果新的实现能通过所有测试,重构成功,否则宣布重构失败,继续修改?

或者说以上两种做法哪个才是真正的重构?

第二个
我知道你后面的问题是什么,不过请你先提出来,我们接着讨论。
0 请登录后投票
   发表时间:2006-04-24  
robbin 写道
TDD是修改功能代码之前,先修改测试来表达你的实现方式,然后运行测试,fail,再修改功能代码,再运行测试,直到测试通过,则证明你的修改完成。

同意,增加或修改一个功能度时确实要这样。
不过我前面问的是“另一顶帽子”——重构
0 请登录后投票
   发表时间:2006-04-24  
我的这个问题你还没解答,呵呵,是在那个帖子里的。
我 写道
我不同意你的地方是:代码也是文档,它是比单元测试更加详细的设计文档;
那么,如果"单元测试文档"细到了和"代码文档"一样的水平,是不是就重复了?
我觉得mack测试的问题就是太细了,于是就引出我上面的问题和前面的另一个重构问题:
用mock测试符合TDD的直觉吗?(我在为一个组件写测试时,想得更多的是它从外面看起来是什么样子,而不会去想也还想不清楚它里面会有哪些机关)


我又有些新的认识,"单元测试文档"更像是详细需求文档,他要描述“做什么”,而不是“怎么做”,“怎么做”还是要在(并且应该只在)"代码文档"中描述。
0 请登录后投票
   发表时间:2006-04-24  
我没去听演讲,看来我错过了提问题的机会,我这里提一个问题:
清楚的代码能反映设计,
测试代码能反映设计。
两者不重复了嘛?

如果一个清晰的设计已经浮现了,先写漂亮的代码,还是埋头先写测试呢?
0 请登录后投票
   发表时间:2006-04-24  
算法的设计和实现,TDD适合嘛?
0 请登录后投票
   发表时间:2006-04-24  
gigix 写道
daquan198163 写道
重构一个组件时,是
先按照新的设计修改对应的单元测试,然后修改组件的实现使其通过所有(包括那个新的)测试,
还是
直接修改组件的实现,如果新的实现能通过所有测试,重构成功,否则宣布重构失败,继续修改?

或者说以上两种做法哪个才是真正的重构?

第二个
我知道你后面的问题是什么,不过请你先提出来,我们接着讨论。


那么,在mock测试与代码实现重复的反映了设计的情况下,重构势必导致原有单元测试失败,重构无法进行下去,因为你已经不能保证重构没有破坏原有功能。

是否会出现这种情况?
如果有,Spring有大量的mock测试,它是怎么解决的?
0 请登录后投票
   发表时间:2006-04-24  
daquan198163 写道
gigix 写道
daquan198163 写道
重构一个组件时,是
先按照新的设计修改对应的单元测试,然后修改组件的实现使其通过所有(包括那个新的)测试,
还是
直接修改组件的实现,如果新的实现能通过所有测试,重构成功,否则宣布重构失败,继续修改?

或者说以上两种做法哪个才是真正的重构?

第二个
我知道你后面的问题是什么,不过请你先提出来,我们接着讨论。


那么,在mock测试与代码实现重复的反映了设计的情况下,重构势必导致原有单元测试失败,重构无法进行下去,因为你已经不能保证重构没有破坏原有功能。

是否会出现这种情况?
如果有,Spring有大量的mock测试,它是怎么解决的?


举个例子来说,LoginController调用UserDAO.loadByName()方法,这个逻辑你用mock来描述了。这是你怎么重构都不会影响到的。如果你重构来重构去,闹到不调用这个方法了,或者调用了两次,那么这就已经是改变了功能实现,不仅仅是重构了。
1 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics