<测试驱动开发实用指南>读书笔记


里面的一些测试技术都很老了,比如junit,现在都注解了.
本来是想跟着里面的电影列表的例子来实践一把的,不过对swing不熟悉,而且没有源代码可以下载的,遇到了一些书面上没有提到的代码,导致中间没法继续下去.
前面几章的一些理论还是不错的,有些以前自己已经知道了,现在再看一遍,有一种顿悟的感觉.
如果能拿java最擅长的web应用做例子就完美了,毕竟做swing应用的人比较少,至少在国内是如此.

写测试的步骤
1.开始先编写一个测试,在测试开头增加一条想让它为true的断言
2.然后为使断言为true创造条件
3.最后需要为创建我们所需要的实际代码.
4.在测试通过之后,开始消除为使测试通过而引入的重复以及其他的一些坏味道代码.

关于重复的一个例子
比如要计算5和3的平均值,我们可以在真实代码中直接返回4,当这个结果通过计算得出结果却返回一个常量时,这就是一种形式的重复.
要去掉重复,首先这样做:
public int getAverage(){return (3+5)/2;}

不过这里仍然存在重复: 3,5与提供给参数是重复的,因此需要通过一个参数用来保存累加的结果:
private int total;
public void add(int i){total +=i;}
public int getAverage(){return total/2;}

这里因为重构的时候而引入了一个常量2,这也是一种重复,需要通过记录每次累加测试来去掉:
private int number;
public void add(int i){total +=i; number ++;}
public int getAverage(){return total/number;}

为了对我们的修改增加一些自信,我们可以增加一些新的测试.

在每次小的改动之后运行测试可以给我们信心和保证,其结果有了下一步工作的勇气,每次一小步一小步地推进,劝告那些完美主义者不要一步到位,循序渐进,不要妄想一口吃成胖子.

在消除重复的过程中,我们还有另外一种思路,就是通过再编写一个测试,然后通过重构保证两个测试通过.
但是无论采用哪种思路,都需要编写另外一个测试,这样既可以驱动归纳工作的进行,也可以进一步验证

重构
重构与TDD两个密切相关的两个方面:
尽可能采用简单的方法来让测试获得通过,然后通过重构来对代码进行整理,其中大部分工作是消除那些为使测试通过引入的重复
当采用TDD之后,那么就有了放手重构的安全网.

重构的时机:
当存在重复的时候
当觉得代码或者代码所表达的意图不够清晰的时候
当觉察到代码可能存在坏味道或者有坏味道的倾向的时候

在你要让某个测试通过的时候,带上编码的帽子,一旦测试通过,你就换顶帽子执行重构来进行清理.

关于意图
当我们在编写测试代码的时候,我们会强迫自己考虑类的接口而不是其实现,我们就有机会站在使用这个类的用户的立场来判定什么才是最有意义的,而不是陷入具体的实现细节中去.

code smell
注释:如果你看到一条注释或者觉得有必要编写一条注释的话,那么请首先要考虑重构或重写代码.
数据类:对某个类数据成员的操作处理应该移到该类的内部,而不是将其遗弃在该类之外,让其他的类对其"蹂躏"修改,这个也体现了封装的原则. "上帝的归上帝,凯撒的归凯撒"
交往不当:这种情况是指一个类对另一个类的内部细节知道太多.要处理这种情况,需要对方法进行迁移,让那些彼此了解的代码处于同一位置. 这样一旦这些内部细节发生变化的时候,其影响的范围会仅在该类的内部,而不会出现扩散.
对于继承也会有类似的问题,子类过多的了解祖先类的实现细节,超出它所了解的,这样就应该把继承改成委派(delegation),或者将这种关系松散化,达到去耦的目的.
类尺寸过大:应该采用抽取出子类或者采用多态来减小之
方法过长:一般情况下是完成的工作太多了.

重构手段(手法千千万,常用就这几种)
extract class:当一个类变的太大或者行为逻辑组织分散时,另一种抽取的原因是由于我们需要某种行为的多种实现.
比如电影列表类有一个功能需要输出一个电影列表,那么我们可以将这个功能拆分到一个单独的类中去,这样就能够很容易的进行替换和改进.
extract interface: 记住一点就好,接口最好是小而专.关于接口的命名,尽量采用通过添加-able,-ible结尾的形容词,但是有时候这种命名没有,这可以使用添加前缀I,比如MovieListable没有意义,我们可以采用IMovieList.
extract method: 通过给一块完成某种功能的代码加注释或者通过空行来清晰代码都是一种潜在的坏味道,需要通过提取方法将代码拆分到各自的方法中.
replace type code withsubclasses: 当我们的类使用类型代码来表示子类型的时候,可以使用该手段来打破那些根据类型编码进行判别的复杂条件判断和switch语句.
replace conditional with polymorphism: 当发现switch语句的爱好ihou可以考虑创建子类来处理各种不同的情况. 从而去掉switch.
form template method: 我的最爱,甚至达到滥用的程度:(
introduce explaining variable:当我们的表达式复杂而且难以立即时,我们可以提取其中的某些部分,把中间结果保存在命名清楚的临时变量中.
replace constructor with factory method:当存在多个用于创建类的不同类型的实例的构造方法时,使用起来不是很容易区分,则需要通过使用静态工厂方法,给每个构造方法起一个有意义的名字.
replace inheritance with delegation: 当子类是特殊种类的超类,或者子类对超类进行扩展而不是复写超类的部分功能时,才使用继承.

设计模式应该是重构的目标,应该通过重构逐渐引入设计模式.

如果发现了给参数赋值的代码,那么应该使用临时变量取而代之.

在开始编码的时候,我们应该强迫自己采用最简单的做法.

注释
注释存在的目的是为了掩盖代码的臭味.
正当的注释:
未完成的代码(TODO)
重构尚无法足够清晰
使用了某种非同寻常的算法
使用某种别人发布的算法
性能调整
类注释(解释这个类为什么存在以及用途,要避免编写有关这个类的使用教程)

真正的TDD应该
测试方法应该尽量简短而切中要害
编写一点测试,编写一点代码(test a little,code a little)
不要一口气把所有的测试都写完,然后再编写代码,而要编写一个测试,让其通过,在编写一个测试,再让其运行通过,以此类推.
万事开头难,应该从编写最简单的测试开始.

测试模式
设定前置条件
执行相应的功能
检查后置条件是否满足

TestCase
应该将TestCase视为一种需要需要以完全相同的设置方式来组织测试的方法,而不是用它来组织给定类的测试.
每当你发现setUp中的有些代码使用某些测试方法,而有些代码不使用其他测试方法时,那么就应该认为这个TestCase应该被重构为两个或者多个TestCase.

何谓简单的测试对象
:
正确的处理null值
空集或null对象的行为
递归或迭代结构以及递归或迭代计算的基本情况(没明白)

应该尽量使用assertEquals()
尽量在assert中给出相关的message,没有message也是一种bad smell,偶没这个习惯:(
尽量保持测试方法中的断言最少,这样可以保证测试专一,简洁,容易理解
编写测试从assertXxx()开始.

测试代码的数量与应用代码一样多,要是能多上一半就更好了
力争每个测试方法中有只出现一个断言
测试做得好,调试器用的少
TDD事前需要做大量的工作,这个工作的收益主要体现在减少了后期调试程序的工作量
一旦你找到了TDD的感觉,你就会比直接写代码和调试器来进行开发的工作速度快得多.

XP
xp中的四种变化量:成本,时间,质量,范围
其中任何一个的价值依赖于其他三个的价值,你最多只能控制四个中的三个,而绝对无法同时控制四个.

xp的价值观:沟通,简单,反馈,勇气
沟通在xp中具有至高无上的重要性,其外在表现形式:
结对编程
每天早上一次站立会议
在开放的环境工作
客户与开发团队一起工作
大量使用白板
把都感兴趣的信息放在显著的位置

改进设计
在编程的时候,你先编写一个测试(刚刚能够失败就行),然后编写一小段代码(刚刚能够保证测试运行通过就行),这一过程会产生设计或结构低劣的代码,所以下一步就是通过重构来使代码重新恢复渐渐.

极限编程就是让程序员们来编写程序,谋生,并且从中获得乐趣.

要想拥抱变化,我们就需要采用一种渐进的开发方式,每次只改变系统的一小部分,而不是在一次大的交付中搞定所有的一切.

agile model 敏捷建模的两个基本目的:为理解某一问题而建模(诸如如何设计系统的某一部分);为了传达团队正在完成的工作而建模. 敏捷模型大部分都是临时性的,当这些模型完成他们的使命之后,继续留存已经没有价值. 敏捷模型是一种刚刚够好(just good enough)的模型

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结