TDD问答录

最近被要求强制使用TDD。这里总结下个人的认识。

为什么需要UT测试?

为什么需要测试就不用回答了。为什么选中UT来做TDD呢?第一点是测试的代价。在嵌入式、分布式环境中进行测试的成本(时间成本、资源成本、人力成本等)非常高,而UT的成本相比而言很低。第二点是可用性。直接验证的是实现,非常底层。更加容易定位问题的根源。所在的层次越高,组合膨胀越大。测试就可能存在死角。而且使用UT一般都是非常便捷的,不需要设备,不需要去搭建复杂的环境。第三点是根源问题。我们测试的是自己编写的CODE,UT是对CODE最直接验证形式。

UT测试有哪些实用性?

1. 测试是对预期行为研究的过程。我们可能对解决的问题有了一个大概的认识,但真正固化到代码的时候有许多细节需要考虑。UT测试帮助我们逐步理解问题,并且指导、辅助设计。在设计的过程中引入测试思维,我们就会去考虑类是否去有强内聚性,耦合是否恰当,因为差的设计会让测试也变得很差。

2. 为重构提供安全网。

其实不仅仅是重构了,为维护Bug Fix同样提供安全网。

TDD有两个循环,第一个是CASE FAIL -> CODE -> CASE PASS,这个循环在于实现一个又一个的新的功能。另一个循环:CASE PASS -> REFACTORING -> CASE PASS。这个循环的作用在于改善代码的结构,是从内部质量出发,考虑维护性、扩展性的举措。其实还有一个隐含的循环,对于同一个CASE我们还是会继续前面的第一个循环。因为抛弃这一隐含循环的TDD只是一个理想,写出完备的CASE也是根本做不到的。CASE的完备的过程同样需要CODE完备的过程,两者相辅相成。

第一个循环应该是可部署的。循环的成果应该组织为测试套件,进而提供自动化测试。

另一方面,UT的开发其实也是集体智慧的固化。它让CODE的最初编写者为后续者提供了一道安全屏障,让其他人也能享用。这些其他人可能考虑问题没有那么全面,知识不那么完备。这是TDD遗留下来的最重要的宝贝。

3. UT测试是一种文档。

UT测试是对使用场景的描述性文档。

UT测试是对如何使用代码的最好范例

4. 减少调试时间。

因为UT的使用会让自己更加了解自己的设计,更加熟悉自己的代码。非常容易找到问题的根源。对于修复缺陷和避免缺陷都有很好的效果。

5. UT使开发过程更加可预测

这点还是得益于对设计和代码的掌控程度。因为代码质量和个人知识有了好的提升,对管理层来说开发就更加透明。

TDD为什么指导的是UT?

广义上讲,我们一直都在TDD。写代码,测试,调试,再写,重复的过程就是TDD。桌面编程可以非常方便不停重复这一过程。然后,随着程序规模的扩大,领域不同的限制,各种外部因素决定了我们不能非常便捷的进行测试。而UT给我们来一个方向。UT除去了很多运行环境因素的限制,比如硬件条件、网络条件等。因为无论在哪,UT肯定都是运行在你熟悉的平台,绝大多数都是Windows。

UT非常容易做CI。因为自动化UT成本很低。相比自动化IT而言,成本极其低廉。

我没有代码,写测试测什么呢?

这是一个鸡生蛋蛋生鸡的问题。

直觉是,有了代码才能去测。这种思维认为测试是一种验证,而不是表达意图。反之,TDD其实是一种意图导向的方法。我们应该抛弃传统的测试验证思维,通过测试来表现意图,从而来看我们的意图是否达到。

还有一个经典的事实:问题可能比答案更为复杂(对我们来说最直观的反应是,测试代码往往比实际代码多)。举个例子,e=Σ1/n!。如果给你后面的级数,你来计算。这个问题可能不是那么简单,还有派啦。正是存在很多问题复杂但答案简单的问题,我们才需要去深入研究问题。测试促使我们检查对问题的理解。

UT和集成测试应该如何分配?

这个平衡需要自己把握。充分的UT能够保证函数、类实现的质量。但对于交互的考虑非常少。集成测试的进行,依赖于Mock对象,而且随着集成测试的边界不同,组合出来的测试数量也非常惊人。它们之间的平衡的把握应该根据具体的环境来决定。

其他的一些观点:

如果测试的考虑是周全的,代码也会是完整的,避免了冗余和浪费。测试覆盖率也会很好。

TDD是作用于个体的软件方法,不可作用于团队。代码最终肯定会出自一个人只手,可能还会有其他一两个人的思想在里面,但真正写来下,CHECK IN,应该只有一个人。从这点上讲,TDD作用于个体。

测试代码与产品代码都是同一语言。

TDD可以带来编写代码的心理满足感。一般情况下有副漫画很好的描述了程序员的心理:不明白为什么不过?不明白为什么过了?一直都在疑惑恐惧外加一丝丝兴奋的状态下(大家去找这幅画吧)。TDD的情况:Fail -> PASS。成就感和兴奋明显要多于疑惑和恐惧。从长期的生理角度看,TDD也还是不错。

xUnit,其实可以做一个主题来讲了。针对不同的语言,都有一个xUint。Java、C、C++、C#、Python等等都有。已经成为了UT测试的代名词了。很多讲解TDD的都会使用JUnit。Java的反射、标注提供了很好的机制。

Mock框架可以帮助我们减轻创建Mock的无聊枯燥的工作。

TDD与模式:模式趋向于增加委托来清晰化对象结构,但委托的增加会使测试变得困难。Mock Object可以解决这一问题。

最后讲个笑话:乌龟的下面

一个科学家举行了一个天文学的公开演讲,主要讲地球如何围绕太阳转,太阳如何围绕银河系中心转。研究结束后,一个老太太站起来说:“你刚才说的都是废话,世界其实是由一个乌龟的后背所支撑的一个平板。”

“那这只乌龟站在什么上面呢?”

“你是一个非常聪明的年轻人,乌龟站在乌龟上,一直向下......”

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结