锁定老帖子 主题:什么是“测试驱动开发”
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-04-25
firebody 写道 虽然是有理,但是单纯从上面的代码来看,我有种过渡设计/分离的味道,因为从代码的简洁程度来说,把一个只有十多行代码的method抽出到一个专门的接口,然后再用一个实现类来作,最好才能体现TDD就是设计的好处,我对此持有疑惑。 重构啊重构,重构也是有条件的。 我是觉得,如果这个method不是被多次用到,代码不会经常替换,就没必要这么做。 重构是为了消除bad smell。不是为了重构而重构。 |
|
返回顶楼 | |
发表时间:2006-04-25
charon 写道 firebody 写道 charon 写道 此时,要求Bean实现的equals方法中按照name判等
你这个更不通用了,在简单的bean还行,因为equals就可以搞定。 万一我得bean的状态比较复杂呢? 还需要实现一个对应的equals方法? 而且,为了测试需要写一个符合测试规则的equals方法,感觉也不好吧。 那个bean是自己构造出来的,想要是什么状态就可以设置什么状态。 其实,那个equals不是一个符合测试规则的equals方法,而是一个一般的equals方法。而且,对象状态的判等,必须是可观测状态的判等,而且必须和equals的语义是一致的。这个和测试不测试没关系 在这个例子中,Bean实际上是不需要equals方法的,因为默认的够用了。 如果在构造状态比较多的bean对象的时候,适当注意那些引用对象的使用,默认的也够用了。 这个例子比较简单了,一个简单的Bean,自然可以轻松利用equals进行对象的判等。但是 很多的对象状态往往很复杂,比如一棵树,集合,或者带有关联的对象等等。equals总归是不能覆盖这些状态的。 另外我的习惯是equals和hashcode一起写 ,我性认为他们两个语义应该是一致的,单独写一个equals不大习惯,或许这跟老和HashMap打交道有关系吧。 所以除非又明确的需求特定一个equals的是线,不然我不会为了测试而写上equals方法的。 |
|
返回顶楼 | |
发表时间:2006-04-25
firebody 写道 charon 写道 firebody 写道 charon 写道 此时,要求Bean实现的equals方法中按照name判等
你这个更不通用了,在简单的bean还行,因为equals就可以搞定。 万一我得bean的状态比较复杂呢? 还需要实现一个对应的equals方法? 而且,为了测试需要写一个符合测试规则的equals方法,感觉也不好吧。 那个bean是自己构造出来的,想要是什么状态就可以设置什么状态。 其实,那个equals不是一个符合测试规则的equals方法,而是一个一般的equals方法。而且,对象状态的判等,必须是可观测状态的判等,而且必须和equals的语义是一致的。这个和测试不测试没关系 在这个例子中,Bean实际上是不需要equals方法的,因为默认的够用了。 如果在构造状态比较多的bean对象的时候,适当注意那些引用对象的使用,默认的也够用了。 这个例子比较简单了,一个简单的Bean,自然可以轻松利用equals进行对象的判等。但是 很多的对象状态往往很复杂,比如一棵树,集合,或者带有关联的对象等等。equals总归是不能覆盖这些状态的。 在这个简单的例子中,用默认的equals判等就已经很大的改善了可读性,如果是复杂的例子,假设这个Bean里面有多个引用对象,写一个equals(当然,里面引用对象的equals是由其该对象自己实现的)会给你的测试代码可读性带来质的飞跃(不然,用IArgumentMatcher的测试代码将没有办法看了)。 引用 另外我的习惯是equals和hashcode一起写 ,我性认为他们两个语义应该是一致的,单独写一个equals不大习惯,或许这跟老和HashMap打交道有关系吧。 所以除非又明确的需求特定一个equals的是线,不然我不会为了测试而写上equals方法的。 为了便于测试连更复杂的工作也作了,难道不肯做这么一点事情?要知道,代码是代码,测试代码也是代码,也是要讲可读性的。实际上,所有对于代码的原则,都可以适用到测试代码。只不过真正那么做的人太少了。 |
|
返回顶楼 | |
发表时间:2006-04-25
charon 写道 firebody 写道 charon 写道 firebody 写道 charon 写道 此时,要求Bean实现的equals方法中按照name判等
你这个更不通用了,在简单的bean还行,因为equals就可以搞定。 万一我得bean的状态比较复杂呢? 还需要实现一个对应的equals方法? 而且,为了测试需要写一个符合测试规则的equals方法,感觉也不好吧。 那个bean是自己构造出来的,想要是什么状态就可以设置什么状态。 其实,那个equals不是一个符合测试规则的equals方法,而是一个一般的equals方法。而且,对象状态的判等,必须是可观测状态的判等,而且必须和equals的语义是一致的。这个和测试不测试没关系 在这个例子中,Bean实际上是不需要equals方法的,因为默认的够用了。 如果在构造状态比较多的bean对象的时候,适当注意那些引用对象的使用,默认的也够用了。 这个例子比较简单了,一个简单的Bean,自然可以轻松利用equals进行对象的判等。但是 很多的对象状态往往很复杂,比如一棵树,集合,或者带有关联的对象等等。equals总归是不能覆盖这些状态的。 在这个简单的例子中,用默认的equals判等就已经很大的改善了可读性,如果是复杂的例子,假设这个Bean里面有多个引用对象,写一个equals(当然,里面引用对象的equals是由其该对象自己实现的)会给你的测试代码可读性带来质的飞跃(不然,用IArgumentMatcher的测试代码将没有办法看了)。 引用 另外我的习惯是equals和hashcode一起写 ,我性认为他们两个语义应该是一致的,单独写一个equals不大习惯,或许这跟老和HashMap打交道有关系吧。 所以除非又明确的需求特定一个equals的是线,不然我不会为了测试而写上equals方法的。 为了便于测试连更复杂的工作也作了,难道不肯做这么一点事情?要知道,代码是代码,测试代码也是代码,也是要讲可读性的。实际上,所有对于代码的原则,都可以适用到测试代码。只不过真正那么做的人太少了。 不错,用Matcher会导致代码的可读性非常差。 很多时候,往往会因为这个改变设计。 针对那行代码,其实我看了第三编就已经忍受不了了。 |
|
返回顶楼 | |
发表时间:2006-04-25
引用 不错,用Matcher会导致代码的可读性非常差。 很多时候,往往会因为这个改变设计。 针对那行代码,其实我看了第三编就已经忍受不了了。 但是,我认为对equals的覆写并不认为是你说的"小事情“,一旦覆写了就要保证其语义的正确性。equals作为对象基本语义之一,我个人习惯对此比较谨慎。 默认的往往都能够按照默认的行为工作,但是覆写的话意味着你需要对这个equals的语义和对象的状态作充分的考虑。 不是指小事情,而是相比于从私有代码中抽取出接口并搞定一个实现类而言,实现一个对象的equals方法,那是轻量多了. 最轻量的方法当然是前面提到过的protected化。 |
|
返回顶楼 | |
发表时间:2006-04-25
charon 写道 引用 不错,用Matcher会导致代码的可读性非常差。 很多时候,往往会因为这个改变设计。 针对那行代码,其实我看了第三编就已经忍受不了了。 但是,我认为对equals的覆写并不认为是你说的"小事情“,一旦覆写了就要保证其语义的正确性。equals作为对象基本语义之一,我个人习惯对此比较谨慎。 默认的往往都能够按照默认的行为工作,但是覆写的话意味着你需要对这个equals的语义和对象的状态作充分的考虑。 不是指小事情,而是相比于从私有代码中抽取出接口并搞定一个实现类而言,实现一个对象的equals方法,那是轻量多了. 最轻量的方法当然是前面提到过的protected化。 我删掉了还来不及。赫赫。 本来已经不想就着equals这个问题再讨论下去了,这个问题讨论起来很容易发散 。 不过那些matcher的代码确确实实比较让人费解和难看。 至于我们要讨论什么呢? 回到正题吧。赫赫,我们的结论是什么? |
|
返回顶楼 | |
发表时间:2006-04-26
hehe,应该已经忘记原来在讨论什么了吧。
除非再回过头去顺着看一边。 或者,从标题来看,大概是在说每个人对测试驱动开发的理解? 我的理解是,现在大的场面上把问题域分解到功能也好,用例也好,或者用户故事也好,总之是到一个团队成员可以操控的粒度。然后,根据这些中的某一项确定测试列表(这个列表就是近期工作指南),一项项以测试优先的方式来搞定代码,其间会在这个测试列表上面添加新的东西(实际上是在做问题的分解,或者,也能算是设计)。 但是,用TDD去规范某个方法的内部设计,是我所反对的。这是我和你以及robbin的分歧所在。其实我现在的做法和gigix的观点倒是有点相近的,只不过更加保守一点,就是说可以在测试用例中体现依赖(这个是正常的,而且这种依赖本来也是被注射进去的,或者说是可观测的),但是,仅此而已。是一个半透明的黑盒,但是我还是嫌它太透明了。 还有一点,对接口的分离上,我也持比较保守的态度,hehe |
|
返回顶楼 | |
发表时间:2006-04-26
charon 写道 hehe,应该已经忘记原来在讨论什么了吧。
除非再回过头去顺着看一边。 或者,从标题来看,大概是在说每个人对测试驱动开发的理解? 我的理解是,现在大的场面上把问题域分解到功能也好,用例也好,或者用户故事也好,总之是到一个团队成员可以操控的粒度。然后,根据这些中的某一项确定测试列表(这个列表就是近期工作指南),一项项以测试优先的方式来搞定代码,其间会在这个测试列表上面添加新的东西(实际上是在做问题的分解,或者,也能算是设计)。 但是,用TDD去规范某个方法的内部设计,是我所反对的。这是我和你以及robbin的分歧所在。其实我现在的做法和gigix的观点倒是有点相近的,只不过更加保守一点,就是说可以在测试用例中体现依赖(这个是正常的,而且这种依赖本来也是被注射进去的,或者说是可观测的),但是,仅此而已。是一个半透明的黑盒,但是我还是嫌它太透明了。 还有一点,对接口的分离上,我也持比较保守的态度,hehe 其实你已经说到点上了。 就是小步骤的进行,随时更新todoList和添加新的测试。 我前面写的那个例子说明我还没有完全体会TDD的含义,昨晚我熬夜重新再看了一遍kent beck的TDD实践。 我知道我遗漏了重要的一点:就是小步骤的添加todoList,添加测试代码,然后小步骤的实现功能,再让测试通过的基础上再优化代码以及重构。 拿我前面的例子来说,至少应该在写代码之前把基本需求列出来,然后写出一个基本的todoList,针对基本需求写一个未通过的测试,如果要使得这个测试通过需要好几个步骤或者比较麻烦,应该把问题分解,再列到todoList上,再针对这些被分解的小任务进行小步骤的添加测试--》使测试通过--》优化代码,重返往复,最后把最终的那个复杂的任务干掉。 就那我前免得例子来说。如果那个doSomeInternalLogic逻辑复杂的话,应该把它列出来作为一个单独的任务放到todoList里面去,单独测试。 就像你说得,可以把它作为一个protected方法进行测试。测试通过后,继续进行原来的任务。 我想我要自己要做TDD了。从我现在的做的JPA实现做起吧。 |
|
返回顶楼 | |
发表时间:2006-04-26
就我看来,写单元测试和写实现,都是设计的过程。
单元测试意味着,设计元素的契约部分。实现意味着,设计元素的结构部分。契约是结构必须满足的充分必要条件。 |
|
返回顶楼 | |
发表时间:2006-04-26
从项目角度,TDD比文档驱动成本更高.
|
|
返回顶楼 | |