论坛首页 综合技术论坛

尝试了一下把TDD用到真正的项目中

浏览 17375 次
该帖已经被评为精华帖
作者 正文
   发表时间:2009-09-29  
chenjianjx 写道
所有代码都是受测试用例驱动而产生的,而测试用例之间只有功能上的逻辑联系,没有设计上的联系。
某种设计模式的引入,都是在发生冗余代码后才“不得以而为之”
yuan 写道
chenjianjx 写道
2.TDD提倡的自底向上的设计思路也会搞得很低效。

求这句话的出处。

我在尝试着自顶向下,似乎没问题,而且自顶向下是直接从需求入手,好像更自然。


请恕我愚钝,这个回复我着实没有看明白。

好像正是因为你先写产品代码,再写测试代码,所以开发过程变成了自底向上的过程。如果先写测试,能预见到的只是被测的接口,然后一层层往下深入,细化……所以我比较好奇那句“TDD提倡的自底向上的设计思路“的出处。
11 请登录后投票
   发表时间:2009-09-29   最后修改:2009-09-29
chenjianjx 写道
ok. 我看看mockito好不好用。 我之所以坚持EasyMock,是因为我要用PowerMock来mock静态方法,而PowerMock又必须基于EasyMock.

至于单元测试的重构问题,我指的是修改类名。修改了被测类的类名,测试类的类名不会被自动改掉。

静态方法能不用就不用吧,实在不行就只在静态方法的地方用PowerMock,其它的用mockito
其实mock也应该是只在那几个特定场景下使用,之前讨论过的。

修改测试类的类名能有多频繁、多麻烦?呵呵
0 请登录后投票
   发表时间:2009-09-29  
daquan198163 写道
tuti 写道
chenjianjx 写道
这次的TDD不是那么严格,我并没有先写测试用例再写代码,而只是把单元模块写好之后立即写单元测试,同时注意维护一套Test Suite,确保单元测试的覆盖程度,并作为代码重构后的验收标准。

这不是不严格的问题,你搞的根本不是TDD,充其量维护了一套自动测试用例。
既然你搞的不是TDD,就不要说TDD如何如何。

我认为TDD这种方法之所以有效,本质在于:
它使得我们在开发过程中能不断的从测试用例获得反馈,然后在这些反馈的基础上去调整设计,自然而然的做出简单、合理的设计;
这里的核心是快速反馈,只要我写下一段代码能尽快的被客户(单元测试)使用、运行,并得到客户的反馈(好不好用、正不正确……),我就可以以此为依据来完善设计。
至于这些测试是在代码之前先写出来的还是在之后几分钟写出来的,没有什么大不了的,根据个人喜好、感觉走就行了,比如:
如果写代码时思路很乱,那就先写个测试,从外部思考,思考这个组件从外面开起来是什么样的,想清楚了以后再去想内部实现;然后完善测试、完善实现,如此反复。
反过来,如果我一开始就对实现比较清楚,有很多想法,那就直奔主题去用代码实现这些想法,写出一个最简单的实现后,立刻写个测试来验证,如此反复。


每个人都可以按自己的理解去用自己的方式写程序。
但如果你已经采用了个有明确意义的词,而且也宣称自己采用这种方式,那么就不要往里面参杂自己的私货。
如TDD这样的词,本来就一个严格的流程和做法。
既然做了很多改变,就不要套用一个公认的词汇来说事,否则困扰别人,更是困扰自己。

"daquan198163"说本质,本质最终还是要落到具体实践上的。跟何况你怎么知道,你理解的本质和别人理解的本质是一个东西。用相同的一次词,各人都有不同的理解的例子,在生活中比比皆是。

写了点回帖,只是想提醒楼主,既然是初学TDD,就该按照标准的套路来做,否则很容易误入歧途。
0 请登录后投票
   发表时间:2009-09-29  
tuti 写道
daquan198163 写道
tuti 写道
chenjianjx 写道
这次的TDD不是那么严格,我并没有先写测试用例再写代码,而只是把单元模块写好之后立即写单元测试,同时注意维护一套Test Suite,确保单元测试的覆盖程度,并作为代码重构后的验收标准。

这不是不严格的问题,你搞的根本不是TDD,充其量维护了一套自动测试用例。
既然你搞的不是TDD,就不要说TDD如何如何。

我认为TDD这种方法之所以有效,本质在于:
它使得我们在开发过程中能不断的从测试用例获得反馈,然后在这些反馈的基础上去调整设计,自然而然的做出简单、合理的设计;
这里的核心是快速反馈,只要我写下一段代码能尽快的被客户(单元测试)使用、运行,并得到客户的反馈(好不好用、正不正确……),我就可以以此为依据来完善设计。
至于这些测试是在代码之前先写出来的还是在之后几分钟写出来的,没有什么大不了的,根据个人喜好、感觉走就行了,比如:
如果写代码时思路很乱,那就先写个测试,从外部思考,思考这个组件从外面开起来是什么样的,想清楚了以后再去想内部实现;然后完善测试、完善实现,如此反复。
反过来,如果我一开始就对实现比较清楚,有很多想法,那就直奔主题去用代码实现这些想法,写出一个最简单的实现后,立刻写个测试来验证,如此反复。


每个人都可以按自己的理解去用自己的方式写程序。
但如果你已经采用了个有明确意义的词,而且也宣称自己采用这种方式,那么就不要往里面参杂自己的私货。
如TDD这样的词,本来就一个严格的流程和做法。
既然做了很多改变,就不要套用一个公认的词汇来说事,否则困扰别人,更是困扰自己。

"daquan198163"说本质,本质最终还是要落到具体实践上的。跟何况你怎么知道,你理解的本质和别人理解的本质是一个东西。用相同的一次词,各人都有不同的理解的例子,在生活中比比皆是。

写了点回帖,只是想提醒楼主,既然是初学TDD,就该按照标准的套路来做,否则很容易误入歧途。

这确实是我对TDD的个人理解和活用(可能是乱用),仅供参考!
如果要给这种方法取个名字的话,就叫Test Immediately Development吧。
0 请登录后投票
   发表时间:2009-09-29  
lz千万别再用TDD了...
0 请登录后投票
   发表时间:2009-09-29  
楼主你所做的不是TDD,重构也并非要求你不做任何预先设计。
0 请登录后投票
   发表时间: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的。
0 请登录后投票
   发表时间: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));

所以…真的…能做出什么设计,和用什么方法来设计,关系不太大
0 请登录后投票
   发表时间:2009-09-30  
TDD并不代表着你必须测试完每一个角落。

哪些是应该需要测试的,这个判断是很重要的一件事情。
0 请登录后投票
   发表时间: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代码。

当然还有很多逻辑是没办法分离到实体层上的,比如复杂的查询/统计,这些东西你只能走集成测试。

掌握了这些原则,基本上就够了,我认为。


我还做了个简单更通俗的划分:
易测的代码写出来的就是单元测试,难测的代码需要准备数据库数据的就是集成测试。



6 请登录后投票
论坛首页 综合技术版

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