锁定老帖子 主题:尝试了一下把TDD用到真正的项目中
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-09-29
chenjianjx 写道 所有代码都是受测试用例驱动而产生的,而测试用例之间只有功能上的逻辑联系,没有设计上的联系。 某种设计模式的引入,都是在发生冗余代码后才“不得以而为之” yuan 写道 chenjianjx 写道 2.TDD提倡的自底向上的设计思路也会搞得很低效。 求这句话的出处。 我在尝试着自顶向下,似乎没问题,而且自顶向下是直接从需求入手,好像更自然。 请恕我愚钝,这个回复我着实没有看明白。 好像正是因为你先写产品代码,再写测试代码,所以开发过程变成了自底向上的过程。如果先写测试,能预见到的只是被测的接口,然后一层层往下深入,细化……所以我比较好奇那句“TDD提倡的自底向上的设计思路“的出处。 |
|
返回顶楼 | |
发表时间:2009-09-29
最后修改:2009-09-29
chenjianjx 写道 ok. 我看看mockito好不好用。 我之所以坚持EasyMock,是因为我要用PowerMock来mock静态方法,而PowerMock又必须基于EasyMock.
至于单元测试的重构问题,我指的是修改类名。修改了被测类的类名,测试类的类名不会被自动改掉。 静态方法能不用就不用吧,实在不行就只在静态方法的地方用PowerMock,其它的用mockito 其实mock也应该是只在那几个特定场景下使用,之前讨论过的。 修改测试类的类名能有多频繁、多麻烦?呵呵 |
|
返回顶楼 | |
发表时间:2009-09-29
daquan198163 写道 tuti 写道 chenjianjx 写道 这次的TDD不是那么严格,我并没有先写测试用例再写代码,而只是把单元模块写好之后立即写单元测试,同时注意维护一套Test Suite,确保单元测试的覆盖程度,并作为代码重构后的验收标准。
这不是不严格的问题,你搞的根本不是TDD,充其量维护了一套自动测试用例。 既然你搞的不是TDD,就不要说TDD如何如何。 我认为TDD这种方法之所以有效,本质在于: 它使得我们在开发过程中能不断的从测试用例获得反馈,然后在这些反馈的基础上去调整设计,自然而然的做出简单、合理的设计; 这里的核心是快速反馈,只要我写下一段代码能尽快的被客户(单元测试)使用、运行,并得到客户的反馈(好不好用、正不正确……),我就可以以此为依据来完善设计。 至于这些测试是在代码之前先写出来的还是在之后几分钟写出来的,没有什么大不了的,根据个人喜好、感觉走就行了,比如: 如果写代码时思路很乱,那就先写个测试,从外部思考,思考这个组件从外面开起来是什么样的,想清楚了以后再去想内部实现;然后完善测试、完善实现,如此反复。 反过来,如果我一开始就对实现比较清楚,有很多想法,那就直奔主题去用代码实现这些想法,写出一个最简单的实现后,立刻写个测试来验证,如此反复。 每个人都可以按自己的理解去用自己的方式写程序。 但如果你已经采用了个有明确意义的词,而且也宣称自己采用这种方式,那么就不要往里面参杂自己的私货。 如TDD这样的词,本来就一个严格的流程和做法。 既然做了很多改变,就不要套用一个公认的词汇来说事,否则困扰别人,更是困扰自己。 "daquan198163"说本质,本质最终还是要落到具体实践上的。跟何况你怎么知道,你理解的本质和别人理解的本质是一个东西。用相同的一次词,各人都有不同的理解的例子,在生活中比比皆是。 写了点回帖,只是想提醒楼主,既然是初学TDD,就该按照标准的套路来做,否则很容易误入歧途。 |
|
返回顶楼 | |
发表时间:2009-09-29
tuti 写道 daquan198163 写道 tuti 写道 chenjianjx 写道 这次的TDD不是那么严格,我并没有先写测试用例再写代码,而只是把单元模块写好之后立即写单元测试,同时注意维护一套Test Suite,确保单元测试的覆盖程度,并作为代码重构后的验收标准。
这不是不严格的问题,你搞的根本不是TDD,充其量维护了一套自动测试用例。 既然你搞的不是TDD,就不要说TDD如何如何。 我认为TDD这种方法之所以有效,本质在于: 它使得我们在开发过程中能不断的从测试用例获得反馈,然后在这些反馈的基础上去调整设计,自然而然的做出简单、合理的设计; 这里的核心是快速反馈,只要我写下一段代码能尽快的被客户(单元测试)使用、运行,并得到客户的反馈(好不好用、正不正确……),我就可以以此为依据来完善设计。 至于这些测试是在代码之前先写出来的还是在之后几分钟写出来的,没有什么大不了的,根据个人喜好、感觉走就行了,比如: 如果写代码时思路很乱,那就先写个测试,从外部思考,思考这个组件从外面开起来是什么样的,想清楚了以后再去想内部实现;然后完善测试、完善实现,如此反复。 反过来,如果我一开始就对实现比较清楚,有很多想法,那就直奔主题去用代码实现这些想法,写出一个最简单的实现后,立刻写个测试来验证,如此反复。 每个人都可以按自己的理解去用自己的方式写程序。 但如果你已经采用了个有明确意义的词,而且也宣称自己采用这种方式,那么就不要往里面参杂自己的私货。 如TDD这样的词,本来就一个严格的流程和做法。 既然做了很多改变,就不要套用一个公认的词汇来说事,否则困扰别人,更是困扰自己。 "daquan198163"说本质,本质最终还是要落到具体实践上的。跟何况你怎么知道,你理解的本质和别人理解的本质是一个东西。用相同的一次词,各人都有不同的理解的例子,在生活中比比皆是。 写了点回帖,只是想提醒楼主,既然是初学TDD,就该按照标准的套路来做,否则很容易误入歧途。 这确实是我对TDD的个人理解和活用(可能是乱用),仅供参考! 如果要给这种方法取个名字的话,就叫Test Immediately Development吧。 |
|
返回顶楼 | |
发表时间:2009-09-29
lz千万别再用TDD了...
|
|
返回顶楼 | |
发表时间:2009-09-29
楼主你所做的不是TDD,重构也并非要求你不做任何预先设计。
|
|
返回顶楼 | |
发表时间:2009-09-29
wsgwz_2000 写道 daquan198163 写道 如果是设计上的重构,那修改测试是难免的了,不过也有解决之道,就是把单元测试写得尽量“黑盒”:只断言输入输出,不断言行为。 刚开始实践TDD,有点疑问: 如果测试写得尽量“黑盒”,好像就不太好驱动出开发了 比如这样一个业务逻辑: 客户是VIP的话,打7折,否则不打折 我写的junit是: testCalculateForVIP() { price = calculate(vip, 10); assert price == 7; } 最终写的代码是: service: calculate(user, price) { some code .... discount = userDao.isVIP(user) ? 7 : 10; some code .... } 可让我疑惑的是,这个userDao.isVIP是怎么驱动出来的? 我也觉得奇怪,为什么isVIP不是User的,反而变成UserDao的。 |
|
返回顶楼 | |
发表时间:2009-09-29
引用 testCalculateForVIP() { price = calculate(vip, 10); assert price == 7; } 难道不应该是这样么? assertEquals(7, vip.discount(10)); 而且…其实…难道不应该是这样么? //flower is an item with price 10 assertEquals(7, flower.priceFor(vip)); 所以…真的…能做出什么设计,和用什么方法来设计,关系不太大 |
|
返回顶楼 | |
发表时间:2009-09-30
TDD并不代表着你必须测试完每一个角落。
哪些是应该需要测试的,这个判断是很重要的一件事情。 |
|
返回顶楼 | |
发表时间:2009-09-30
最后修改:2009-09-30
wsgwz_2000 写道 daquan198163 写道 如果是设计上的重构,那修改测试是难免的了,不过也有解决之道,就是把单元测试写得尽量“黑盒”:只断言输入输出,不断言行为。 刚开始实践TDD,有点疑问: 如果测试写得尽量“黑盒”,好像就不太好驱动出开发了 比如这样一个业务逻辑: 客户是VIP的话,打7折,否则不打折 我写的junit是: testCalculateForVIP() { price = calculate(vip, 10); assert price == 7; } 最终写的代码是: service: calculate(user, price) { some code .... discount = userDao.isVIP(user) ? 7 : 10; some code .... } 可让我疑惑的是,这个userDao.isVIP是怎么驱动出来的? TDD有一个原则:尽量让你的代码易测。 怎么评判易测和难测的呢? 其实很简单,那就看你准备测试数据的步骤是否很难。 当然现在有了Mock框架,准备数据这块要来的简单得多,但是相对更简单的那就是直接在模型上准备数据而不需要mock,所以从这个角度来看,你需要做的改动就是驱动你的设计往尽量这块来走,走不了,再考虑Mock。 还给个更通俗的逻辑: 业务逻辑无非位于这三个地方: domain层,service层,dao层。 当然,我一般吧service和dao合并为一层。其实就是:domain,service。 domain的含义还要更广泛一些,它包括 实体或者utils,或者helper类。 为了让代码更易测,很简单的一个原则就是尽量把足够多的代码往domain层移动,利用重构慢慢你就发现 很多service中写的代码其实就是模型上的一个业务语义的动作,为何你会有这个发现呢? 因为你一直想把service中的尽量多的代码往domain层移动啊,这个宗旨迫使你在做正确方向的重构啊,当然还会有很多原则来指导你将这个方法到底是放在实体A还是实体B上呢? 当然这里不讨论这个问题, 到最后你就会发现,这些代码在你做开发的时候就可以 写相应的纯粹的单元测试,不需要准备数据库数据,也不需要写mock代码。 当然还有很多逻辑是没办法分离到实体层上的,比如复杂的查询/统计,这些东西你只能走集成测试。 掌握了这些原则,基本上就够了,我认为。 我还做了个简单更通俗的划分: 易测的代码写出来的就是单元测试,难测的代码需要准备数据库数据的就是集成测试。 |
|
返回顶楼 | |