测试驱动开发_测试驱动开发四个步骤 - CSDN
精华内容
参与话题
  • 浅谈测试驱动开发(TDD)

    千次阅读 2016-04-17 09:12:46
    浅谈测试驱动开发(TDD) 测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量。本文从开发人员使用的角度,介绍了 TDD 优势、原理、过程、原则、测试技术...

    浅谈测试驱动开发(TDD)

    测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量。本文从开发人员使用的角度,介绍了 TDD 优势、原理、过程、原则、测试技术、Tips 等方面。

    李群 (liqun@nsfocus.com)www.ihere.org

    李群当前关注于网络安全产品的开发、研究;软件开发过程等方面。您可以通过 liqun@nsfocus.com和他联系。



    2004 年 11 月 19 日

    背景

    一个高效的软件开发过程对软件开发人员来说是至关重要的,决定着开发是痛苦的挣扎,还是不断进步的喜悦。国人对软件蓝领的不屑,对繁琐冗长的传统开发过程的不耐,使大多数开发人员无所适从。最近兴起的一些软件开发过程相关的技术,提供一些比较高效、实用的软件过程开发方法。其中比较基础、关键的一个技术就是测试驱动开发(Test-Driven Development)。虽然TDD光大于极限编程,但测试驱动开发完全可以单独应用。下面就从开发人员使用的角度进行介绍,使开发人员用最少的代价尽快理解、掌握、应用这种技术。下面分优势,原理,过程,原则,测试技术,Tips等方面进行讨论。


    1. 优势

    TDD的基本思路就是通过测试来推动整个开发的进行。而测试驱动开发技术并不只是单纯的测试工作。

    需求向来就是软件开发过程中感觉最不好明确描述、易变的东西。这里说的需求不只是指用户的需求,还包括对代码的使用需求。很多开发人员最害怕的就是后期还要修改某个类或者函数的接口进行修改或者扩展,为什么会发生这样的事情就是因为这部分代码的使用需求没有很好的描述。测试驱动开发就是通过编写测试用例,先考虑代码的使用需求(包括功能、过程、接口等),而且这个描述是无二义的,可执行验证的。

    通过编写这部分代码的测试用例,对其功能的分解、使用过程、接口都进行了设计。而且这种从使用角度对代码的设计通常更符合后期开发的需求。可测试的要求,对代码的内聚性的提高和复用都非常有益。因此测试驱动开发也是一种代码设计的过程。

    开发人员通常对编写文档非常厌烦,但要使用、理解别人的代码时通常又希望能有文档进行指导。而测试驱动开发过程中产生的测试用例代码就是对代码的最好的解释。

    快乐工作的基础就是对自己有信心,对自己的工作成果有信心。当前很多开发人员却经常在担心:“代码是否正确?”“辛苦编写的代码还有没有严重bug?”“修改的新代码对其他部分有没有影响?”。这种担心甚至导致某些代码应该修改却不敢修改的地步。测试驱动开发提供的测试集就可以作为你信心的来源。

    当然测试驱动开发最重要的功能还在于保障代码的正确性,能够迅速发现、定位bug。而迅速发现、定位bug是很多开发人员的梦想。针对关键代码的测试集,以及不断完善的测试用例,为迅速发现、定位bug提供了条件。

    我的一段功能非常复杂的代码使用TDD开发完成,真实环境应用中只发现几个bug,而且很快被定位解决。您在应用后,也一定会为那种自信的开发过程,功能不断增加、完善的感觉,迅速发现、定位bug的能力所感染,喜欢这个技术的。

    那么是什么样的原理、方法提供上面说的这些好处哪?下面我们就看看TDD的原理。


    2. 原理

    测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码。也就是说在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能,直到完全部功能的开发。

    我们这里把这个技术的应用领域从代码编写扩展到整个开发过程。应该对整个开发过程的各个阶段进行测试驱动,首先思考如何对这个阶段进行测试、验证、考核,并编写相关的测试文档,然后开始下一步工作,最后再验证相关的工作。下图是一个比较流行的测试模型:V测试模型。

    【图 V测试模型】
    【图 V测试模型】

    在开发的各个阶段,包括需求分析、概要设计、详细设计、编码过程中都应该考虑相对应的测试工作,完成相关的测试用例的设计、测试方案、测试计划的编写。这里提到的开发阶段只是举例,根据实际的开发活动进行调整。相关的测试文档也不一定是非常详细复杂的文档,或者什么形式,但应该养成测试驱动的习惯。

    关于测试模型,还有X测试模型。这个测试模型,我认为,是对详细阶段和编码阶段进行建模,应该说更详细的描述了详细设计和编码阶段的开发行为。及针对某个功能进行对应的测试驱动开发。

    【图 X测试模型】
    【图 X测试模型】

    基本原理应该说非常简单,那么如何进行实际操作哪,下面对开发过程进行详细的介绍。


    3. 过程

    软件开发其他阶段的测试驱动开发,根据测试驱动开发的思想完成对应的测试文档即可。下面针对详细设计和编码阶段进行介绍。

    测试驱动开发的基本过程如下:

    1) 明确当前要完成的功能。可以记录成一个 TODO 列表。

    2) 快速完成针对此功能的测试用例编写。

    3) 测试代码编译不通过。

    4) 编写对应的功能代码。

    5) 测试通过。

    6) 对代码进行重构,并保证测试通过。

    7) 循环完成所有功能的开发。

    为了保证整个测试过程比较快捷、方便,通常可以使用测试框架组织所有的测试用例。一个免费的、优秀的测试框架是 Xunit 系列,几乎所有的语言都有对应的测试框架。我曾经写过一篇文章介绍CppUnit的文章( http://www.ibm.com/developerworks/cn/linux/l-cppunit/index.html)。

    开发过程中,通常把测试代码和功能代码分开存放,这里提供一个简单的测试框架使用例子,您可以通过它了解测试框架的使用。下面是文件列表。

    	project/				项目主目录
    	project/test			测试项目主目录
    	project/test/testSeq.cpp		测试seq_t 的测试文件,对其他功能文件的测试文件复制后修改即可
    	project/test/testSeq.h
    	project/test/Makefile			测试项目的 Makefile 
    	project/test/main.cpp			测试项目的主文件,不需要修改
    	project/main.cpp		           项目的主文件
    	project/seq_t.h			功能代码,被测试文件
    	project/Makefile		           项目的 Makefile

    主要流程基本如此,但要让你的代码很容易的进行测试,全面又不繁琐的进行测试,还是有很多测试原则和技术需要考虑。


    4. 原则

    测试隔离。不同代码的测试应该相互隔离。对一块代码的测试只考虑此代码的测试,不要考虑其实现细节(比如它使用了其他类的边界条件)。

    一顶帽子。开发人员开发过程中要做不同的工作,比如:编写测试代码、开发功能代码、对代码重构等。做不同的事,承担不同的角色。开发人员完成对应的工作时应该保持注意力集中在当前工作上,而不要过多的考虑其他方面的细节,保证头上只有一顶帽子。避免考虑无关细节过多,无谓地增加复杂度。

    测试列表。需要测试的功能点很多。应该在任何阶段想添加功能需求问题时,把相关功能点加到测试列表中,然后继续手头工作。然后不断的完成对应的测试用例、功能代码、重构。一是避免疏漏,也避免干扰当前进行的工作。

    测试驱动。这个比较核心。完成某个功能,某个类,首先编写测试代码,考虑其如何使用、如何测试。然后在对其进行设计、编码。

    先写断言。测试代码编写时,应该首先编写对功能代码的判断用的断言语句,然后编写相应的辅助语句。

    可测试性。功能代码设计、开发时应该具有较强的可测试性。其实遵循比较好的设计原则的代码都具备较好的测试性。比如比较高的内聚性,尽量依赖于接口等。

    及时重构。无论是功能代码还是测试代码,对结构不合理,重复的代码等情况,在测试通过后,及时进行重构。关于重构,我会另撰文详细分析。

    小步前进。软件开发是个复杂性非常高的工作,开发过程中要考虑很多东西,包括代码的正确性、可扩展性、性能等等,很多问题都是因为复杂性太大导致的。极限编程提出了一个非常好的思路就是小步前进。把所有的规模大、复杂性高的工作,分解成小的任务来完成。对于一个类来说,一个功能一个功能的完成,如果太困难就再分解。每个功能的完成就走测试代码-功能代码-测试-重构的循环。通过分解降低整个系统开发的复杂性。这样的效果非常明显。几个小的功能代码完成后,大的功能代码几乎是不用调试就可以通过。一个个类方法的实现,很快就看到整个类很快就完成啦。本来感觉很多特性需要增加,很快就会看到没有几个啦。你甚至会为这个速度感到震惊。(我理解,是大幅度减少调试、出错的时间产生的这种速度感)


    5. 测试技术

    5.1. 测试范围、粒度

    对哪些功能进行测试?会不会太繁琐?什么时候可以停止测试?这些问题比较常见。按大师 Kent Benk 的话,对那些你认为应该测试的代码进行测试。就是说,要相信自己的感觉,自己的经验。那些重要的功能、核心的代码就应该重点测试。感到疲劳就应该停下来休息一下。感觉没有必要更详细的测试,就停止本轮测试。

    测试驱动开发强调测试并不应该是负担,而应该是帮助我们减轻工作量的方法。而对于何时停止编写测试用例,也是应该根据你的经验,功能复杂、核心功能的代码就应该编写更全面、细致的测试用例,否则测试流程即可。

    测试范围没有静态的标准,同时也应该可以随着时间改变。对于开始没有编写足够的测试的功能代码,随着bug的出现,根据bug补齐相关的测试用例即可。

    小步前进的原则,要求我们对大的功能块测试时,应该先分拆成更小的功能块进行测试,比如一个类A使用了类B、C,就应该编写到A使用B、C功能的测试代码前,完成对B、C的测试和开发。那么是不是每个小类或者小函数都应该测试哪?我认为没有必要。你应该运用你的经验,对那些可能出问题的地方重点测试,感觉不可能出问题的地方就等它真正出问题的时候再补测试吧。

    5.2. 怎么编写测试用例

    测试用例的编写就用上了传统的测试技术。

    • 操作过程尽量模拟正常使用的过程。
    • 全面的测试用例应该尽量做到分支覆盖,核心代码尽量做到路径覆盖。
    • 测试数据尽量包括:真实数据、边界数据。
    • 测试语句和测试数据应该尽量简单,容易理解。
    • 为了避免对其他代码过多的依赖,可以实现简单的桩函数或桩类(Mock Object)。
    • 如果内部状态非常复杂或者应该判断流程而不是状态,可以通过记录日志字符串的方式进行验证。

    6. Tips

    很多朋友有疑问,“测试代码的正确性如何保障?是写测试代码还是写测试文档?”这样是不是会陷入“鸡生蛋,蛋生鸡”的循环。其实是不会的。通常测试代码通常是非常简单的,通常围绕着某个情况的正确性判断的几个语句,如果太复杂,就应该继续分解啦。而传统的开发过程通常强调测试文档。但随着开发节奏的加快,用户需求的不断变化,维护高层(需求、概要设计)的测试文档可以,更低层的测试文档的成本的确太大了。而且可实时验证功能正确性的测试代码就是对代码最好的文档。

    软件开发过程中,除了遵守上面提到的测试驱动开发的几个原则外,一个需要注意的问题就是,谨防过度设计。编写功能代码时应该关注于完成当前功能点,通过测试,使用最简单、直接的方式来编码。过多的考虑后期的扩展,其他功能的添加,无疑增加了过多的复杂性,容易产生问题。应该等到要添加这些特性时在进行详细的测试驱动开发。到时候,有整套测试用例做基础,通过不断重构很容易添加相关特性。

    参考资料


    http://www.ibm.com/developerworks/cn/linux/l-tdd/

    展开全文
  • 测试驱动开发,单元测试,工程实践、极限编程、敏捷开发
  • 测试驱动开发的艺术电子完整版,内容完整,清晰,有目录,完整代码和详细的步骤
  • 测试驱动开发优缺点

    千次阅读 2015-06-30 19:16:03
     近期学习了TDD(测试驱动开发),但是由于没有亲身使用太多,所以说不出太多感受,但是看到了一篇博客,觉得讲的挺好的,跟大家分享下。 正题  不觉间,采用测试驱动开发(Test Driven Development)半年有余,...

    前言

         近期学习了TDD(测试驱动开发),但是由于没有亲身使用太多,所以说不出太多感受,但是看到了一篇博客,觉得讲的挺好的,跟大家分享下。

    正题

        不觉间,采用测试驱动开发(Test Driven Development)半年有余,自从看了Robert Martin的《敏捷软件开发:原则、模式与实践》, 就忍不住想实践一下,亲身体会书中描述的美妙景象。恰逢项目中一个全新功能交由我负责,开发周期也不是十分急迫,就拿这个新功能当回小白鼠,遵循书中的实 践方法开始使用测试驱动开发。


      随着开发的不断深入,测试驱动开发的实践渐入佳境,对其认识也从开始时的顶礼膜拜逐渐回归理性,在为其优长欢欣鼓舞的同时,更理解了其不足或者说是不具备的能力。


      测试驱动开发意味着不再是从需求分析与概要设计/详细设计后直接进入到实现代码的编写,而是转而根据需求分析和概要设计进行测试用例的设计与测 试代码的编写,通过测试代码硬性规定了实现代码所必须满足的功能需求、容错能力。编写实现代码的唯一目的就是使所有测试用例成功运行,任何测试用例的失败 都意味着实现代码存在功能缺陷或者逻辑错误。之后就是不断重复--修改或增加代码再运行所有测试用例检查结果--的迭代过程。


      看上去很简单的一个过程,却与传统的开发格格不入,惯性的力量导致开始时很难从传统过程的思维方式中摆脱出来。在做完需求分析与概要设计,开始 设计测试用例与编写测试代码时手足无措,只能摸着石头过河,试着去做去写,然后通过测试驱动开发的迭代过程去观察,去体会,去修改,去适应,然后再重复迭 代过程。就这样,渐渐理解了测试驱动开发这种“进化->测试->反馈->再进化->…”迭代循环的自然与强大,从测试中得到的反 馈不仅说明实现代码的完备和健全与否,也给人不断进步之感,似乎总是在脚踏实地的前进着。因为在迈开每一步之前,都知道已有的工作经受了测试的检验,就具有相应的信心。即使发现有更好更优秀的设计实现,也可以放心大胆的彻底重构,因为有测试用例和测试代码作监督作守候。


      此外,测试驱动开发也改变了原先开发的视角,不再是直接编写实现代码,而是转而从旁观者和使用者的角度设计测试用例和测试代码,总是能够发现很多原先忽略的因素和条件。


      测试驱动开发的优势在《敏捷软件开发:原则、模式与实践》已有详述,我所感受到的有:


      1. 有助于设计简单清晰而易用的接口。因为总是先有测试代码,才编写实现代码,意味着总是从使用者的角度设计接口,只有简单易用的接口才方便测试时调用,所以我几乎是“被迫”去努力设计简单易用的接口,因为我就是第一个使用者。


      2. 模块切分的足够小但是模块间保持极低的耦合度。为了方便测试,我总是尽力把重复的逻辑剥离出来,单独构建模块进行测试;并且尽量减少模块间的耦合,保持模 块相对独立和功能完备。如果模块过大,或者模块间强耦合,写测试用例与代码时就会困难重重,笨重复杂。因为总是先写测试代码,眼前的利益高于一切,将来的 实现代码必然要迁就目前测试的需要。于是,我又“不知不觉”设计出小粒度模块,且模块间耦合度低的实现。


      3. 肆无忌惮的重构。因为有测试用例和测试代码作担保,我终于能够从小心翼翼心惊胆战的重构中解脱出来,只要是更好的实现和设计,我都愿意尝试,管它呢,反正 多跑几遍测试就知道重构的结果如何了。可以说,测试驱动开发鼓励代码的不断进化,即使测试已经全部通过,也可以通过大胆重构来改进设计与实现。尽管此时是 否还需要再重构见仁见智,至少提供了一个可能,我是很喜欢这一点。


      4. 测试代码是“活”的软件文档,它硬性规定了实现代码必须满足的需求,达不到就报错。传统的文本文档比之就苍白无力多了,“应该”,“必须”,这些字眼对程 序员有多少约束力?而且测试代码总是能与实现代码保持新鲜同步,传统文档写完后经常被上传服务器束之高阁,很少人问津,随着开发组内人员的变动,往往最后 就湮没在服务器的故纸堆之中了。


      测试驱动开发毕竟不是软件银弹,也不存在这样的银弹,它也有力不能及的地方:


      1. 测试驱动开发不可能让人立即具有设计出优美解决方案的能力,或者说是优秀的分析与解决问题的能力。TDD不是Test Driven Design。它只是一个过程,也许可以帮助你发现并帮助你实现优美的解决方案,但是它不能变魔术一样,只要学会了就变出一个优美的设计出来,优秀的分析 问题与解决问题的能力还是要靠不断地学习与借鉴他人成就才能得到提高。


      2. 测试驱动开发不能节省开发投入,也很少能够节省开发周期。测试开发所编写的大量测试代码都是要投入时间与精力的,我现在的代码统计显示,测试代码与实现代 码的比例基本在3:2,即使因为测试驱动开发能得到一个简洁的设计,也不能弥补测试代码的工作量。当然,测试代码可以一定程度保证高质量的实现代码,从而 减少后期软件测试与修正缺陷的工作周期,并进一步在软件发布后减少代码修正维护的工作量。但至少在开发阶段,两相抵消,开发周期并不能有明显改善,如果是 第一次采纳测试驱动开发,甚至会延长开发周期。


      3. 测试驱动开发不能杜绝所有的软件缺陷。尽管测试驱动开发通过测试约束,减少了程序员犯错和遗忘的可能,但是这只是把问题从实现代码部分地转移到了测试代 码。测试用例的完备与否,测试代码本身逻辑的正确与否都依赖于程序员,糟糕的测试用例设计和测试代码实现可能自顾不暇,也就失去了监督实现代码的能力。我 就见过有程序员在测试代码中读取实现代码生成的数据,再直接拿之来验证实现代码生成的数据,x必然恒等于x,这样的测试逻辑必然成功,但是毫无意义。


      对测试驱动开发认识的深入,让我更能合理运用它,扬其长避其短,充分享受其带来的便利。


      测试驱动开发带给我前所未有的软件开发体验,人们都说TDD是传染病,一旦接触就无法自拔。我想说,是的,但我心甘情愿被传染,无怨无悔,无忧无虑。借用一句英文:There’s nothing to fear, TDD is with us, amen.


    展开全文
  • tdd(测试驱动开发)的概述

    千次阅读 2019-06-23 10:52:35
    简单介绍tdd(测试驱动开发)的方法,思想与一些best practices

    最近的工作的项目,使用了tdd(test-driven development测试驱动开发)的开发模式。
    这两几年大概听说了无数种xxx-dd, ddd, tdd, atdd, bdd, fdd, udd各种名词眼花缭乱,当然很多dd其实也有相互借鉴(抄袭)的部分。不过仔细想来测试驱动应该也是很久以前就有的一种技术了,21世纪早已经是测试自动化的年代。tdd讲师还推荐了有一本书叫《XUnit Test Patterns: Refactoring Test Code》。书早在2007年就出版了,自己也算是后知后觉。

    接触tdd之前的想法。

    代码只要写的漂亮就行了。代码写得漂亮了,看代码就全懂了,文档的什么都是垃圾!
    代码写得好,可读性强,造成问题的概率就会低。
    程序设计是很重要的,如果不做设计就开写,结果就是产生一坨垃圾!所以不要急着去写代码,写做好设计,理清思路后再动手!
    突然有一天以为tdd教洗脑能手出现,提出了令人毁三观的观点----不要在写代码之前去做设计!
    什么?!为什么会这样?!
    展开之前,先简单介绍一下tdd的方法论

    tdd的步骤

    tdd

    1. 先写测试代码,并执行,得到失败结果

    比如我们要写实现一个功能,当输入值大于等于0时返回true,当输入值小于0时返回false。
    按照tdd的步骤先要写一个肯定会失败的测试,注意测试代码先于实现代码,因为还没有实现代码,所以测试必定是失败的。
    大概是下面这种样子。

    • 测试代码
    @Test
    void greaterEqualThan0Test{
      // given 
      int input = 2;
      // when
      boolean result = greaterEqualThan0(input);
      // then
      assertThat(result, equalTo(true));
    }
    
    • 实现代码(未完成)
    public boolean greaterEqualThan0(int input){
      return false;
    }
    

    运行测试,得到红灯。

    2. 写实现代码让测试通过

    先不管任何设计啥的,只要让测试过了就行,随便乱写。比如下面的的代码。

    • 实现代码
    public boolean greaterEqualThan0(int input){
      return true;  // 我勒个*这都可以!?
    }
    

    运行测试,得到绿灯。

    3. 重构代码,并保证测试通过。

    实现太简单。没什么能重构的,跳过。

    4. 反复实行这个步骤 测试失败 -> 测试成功 -> 重构

    再写一个测试

    @Test
    void greaterEqualThan0Test_2{
      // given 
      int input = -1;
      // when
      boolean result = greaterEqualThan0(input);
      // then
      assertThat(result, equalTo(false));
    }
    

    执行测试,失败。
    写实现。

    • 实现代码(重构前)
    public boolean greaterEqualThan0(int input){
      if(input >= 0){
        return true;
      }
      return false;
    }
    

    重构
    因为有了自动测试的保护,你可以尽情地修改之前看得不爽的代码,为所欲为。
    其结果可能是下面的情况。

    • 实现代码(重构后)
    public boolean greaterEqualThan0(int input){
      return intput >= 0;
    }
    

    当然我们还可以把测试代码也重构一下。

    • 测试代码
    @ParameterizedTest
    @CsvSource({"2, true",
                "-1, false"
      })
    void greaterEqualThan0Test(int input, boolean expectedResult){
      // given 
    
      // when
      boolean result = greaterEqualThan0(input);
      // then
      assertThat(result, equalTo(expectedResult));
    }
    

    之后也可以多加case

    • 测试代码
    @ParameterizedTest
    @CsvSource({"2,  true",
                "-1, false",
                "0,  true"
      })
    void greaterEqualThan0Test(int input, boolean expectedResult){
      // given 
    
      // when
      boolean result = greaterEqualThan0(input);
      // then
      assertThat(result, equalTo(expectedResult));
    }
    

    这种小学生都能完成的代码,可能给人杀鸡焉用牛刀的感觉。不过相信能给大家一种比较直观的印象。
    需要注意一点,就是在第一步写测试代码的时候,一定要运行测试,确认测试是失败的。我觉得有两个目的。

    • 思考你应该如何验证你的程序,帮助你写出一种比较容易验证的代码。
    • 防止测试本身是错误的。
      比如,你可能一不小心手滑写了下面一个测试。
    // given 
    int input = 2;
    // when
    boolean result = greaterEqualThan0(input);
    // then
    assertThat(true, equalTo(true));
    

    这个测试是没有任何意义的,对你的代码重构起不到任何保护的作用。

    另外请注意!本人在写实现大码之前没有做设计!!!,虽然很有造作的嫌疑。。。

    tdd的思想(个人观点)

    首先声明一下这些只代表本人的观点,主要来自于工作与上一些tdd教徒的交流体会。我不是很确定tdd是否有这样系统的原理。

    1. 测试代码先于实现代码,不允许有没有测试代码的实现代码。

    这个字面意思还是很好理解。在这样的开发方式中,你所开发的功能绝对会被测试保护的。
    如下图。
    在这里插入图片描述
    而下面这种情况是tdd所希望避免的。实现的代码的范围超出了测试代码。而超出的部分没有受到测试的保护,是容易产生bug的部分。
    在这里插入图片描述
    有一些开发者可能喜欢先写代码,后写测试。从tdd的角度来说,这样何容易造成上图的结果。因为当你的实现代码变复杂了之后,你可能遗漏一些测试的case。
    另外,先开发后测试的另一个缺点是写测试的难度会变高。这应该也很好理解,当你设计程序时,如果你没有考虑可测试性的要素,很可能最终的代码会变得超难测。比如一个没有返回值,但有副作用的函数,写测试就会比较麻烦。(当然我不是说写函数一定要有返回值 (´ε`;))

    2. 好的设计是不断的重构中实现的,想要在写代码前就作出好的设计是很有难度的。与其在最初去做好的设计,不如不断的重构,使代码进化。

    其实这个思想应该是Emergent Design。本身和tdd并无直接关系。但由于培训师讲师非常提倡这个思想,结果不慎被洗脑,在这里讲一下。
    这个思想的缘由大致是,程序用来实现具体的业务逻辑,当这也业务逻辑是复杂的情况下,理解业务逻辑是需要花费时间的。
    当程序员开始开发,处于程序设计阶段时,对业务地理解是相对少和浅的。对业务的理解是随着开发的推进而增加。
    在这里插入图片描述
    这种情况特别在维护现有产品时更容易显现。(一个原本已经能够运行的成品,因为一些功能的修改或增加,突然一些地方就跑不了了。。。仔细一查,发现了一些不为人知的业物逻辑。。。orz)
    当你在对业务的理解相对少的前期阶段,做出好的设计是很困难的。而在开发有一定进展后,对业务有了更好的理解,这时候做出的设计就会更符合需求更加合理。
    另外ttd(也不一定源于tdd)把产品品质分成两种。

    1. 外部品质: 保证程序根据需求运行,不出错
    2. 内部品质: 产品的设计,可维护性等
      在这里插入图片描述
      外部品质是通过测试来保证。而内部品质则是通过重构来实现更好的设计。、

    3. 不做过度的设计(over engineering)

    这个观点其实和Emergent Design也是关联的。程序开发的前期阶段为了程序更好的扩展性,往往做了一些超越目前需求的设计。貌似是有数据支持,不过因为本人太懒,懒得找。维基百科上说是,这些为了扩展性所做的设计,最终得到使用的不足10%,所以90%的时间都会被浪费。
    另外具有扩展型的设计,因为这些设计的部分很可能现阶段没有被使用,反而会迷惑其他的开发者。(比如你设计了一张数据表,然后为了将来不对数据表做更改,加了一列 colum_for_future_extension)
    对此还有一个标语专门提倡这种观点。YAGNI。You ain’t gonna need it!
    所以tdd所期望的是程序的实现能与现阶段的需求完全契合,不做过度的设计。你要做的是根据现在的需求写测试,让测试通过,并此需求上把代码设计的更好更合理。因为测试为先,测试又根据需求来写。这样的话会比较难产生over engineering。

    一些推荐的实践方法(best practices)

    根据上面介绍的思想,tdd还有有一些推荐的实践方法。

    1. baby step

    tdd希望程序员开发时,频繁地实行这个开发循环。
    tests failed -> tests passed -> refactoring
    不要写了半小时测试,然后运行一下,然后又开发半小时。
    频繁地运行测试,来验证自己的实现或者对业务的理解是否正确。步步为营,循序渐进。如果测试出错了,回退到之前测试通过的状态也比较容易。

    2. 不同时做两件事情

    tdd的开发步骤分为,tests failed, tests passed, refactoring,每个步骤只做这个步骤该做的事情。
    比如在test passed的步骤里,你要做的就是让测试尽快通过,而不是一边想着让测试通过,一遍还要重构。结果在测试还没通过的情况下就先重构,结果就是那个步骤出了错就比较难判断。
    类似地,在写失败测试时,不要就急着把业务逻辑给实现了。

    3. 通过发现code smell来改进代码

    好的代码是很难描述的,而且众说纷纭。不过什么代码是不好的,倒是有一定共识,并且容易具体化的东西。
    比如一个方法写得太长了,很有可能需要把这个方法拆分成几个抽象度更高的方法。
    不好的代码有一个专门的术语,code smell(代码发臭了。。。)。在此引用一下维基百科。
    code smell
    在tdd的第二步让测试通过后,第三步refactoring,code smell便能帮助你发现需要refactoring的地方。

    结语

    刚受到tdd教洗脑时,感觉这种理论根本就是毁灭性的,反智的!有点像达尔文的《进化论》。《进化论》之前人们普遍接受神创思想。多样与复杂的生物,只有神能够来创造。而《进化论》认为,不需要神的帮助,生物也能自主从简单演化到复杂,从单细胞演到多细胞。如果很极端地去遵循tdd的教义(我的例子中其实也没有严格遵循啦 ^ ^ ),那在编程时不用作任何设计,不断通过重构来演化,最终得到的会是高品质的程序。
    不过我最终还是入教了。。。那么tdd就是是不是个神器?之后的文章想谈谈在实践tdd时遇到的问题与感想。

    展开全文
  • 测试驱动开发

    千次阅读 2019-06-20 12:42:58
    测试驱动开发 概述 极限编程是一个轻量级的、灵巧的软件开发方法,同时它也是一个非常严 谨和周密的方法,它从 4 个基本方面对软件项目进行改善:交流、简易、反馈 和勇气。测试驱动开发则是极限编程的最佳...

    测试驱动开发

    概述

    极限编程是一个轻量级的、灵巧的软件开发方法,同时它也是一个非常严

    谨和周密的方法,它从 4 个基本方面对软件项目进行改善:交流、简易、反馈

    和勇气。测试驱动开发则是极限编程的最佳实践之一。它是编程时使用的技术,

    要求在编写任何产品代码之前,首先编写用于定义产品代码行为的测试。采用

    测试驱动开发,我们将得到简单、清晰、高质量的代码。 MVC 模式是一个复杂的架构模式,它将一个应用的输入、处理、输出流程按照 Model、View、Controller 的方式进行分离,使得产品的结构清晰,易于 维护,有利于软件工程化管理。 Kent Beck 作为极限编程的创始人,提出了测试驱动开发的部分方法,并成功地应用于许多小型的项目中。但是,测试驱动开发在许多系统中应用还存在一定的难度,比如具有图形用户界面和多层架构的系统。所以,基于设计模式的测试驱动开发的研究成为国内外软件工程方向研究者们的课题之一。

    反馈是 XP 的四个基本的价值观之一——在软件开发中,只有通过充分的测试才能获得充分的反馈。XP 中提出的测试,在其它软件开发方法中都可以见到,比如功能测试、单元测试、系统测试和负荷测试等;与众不同的是,XP 将测试结合到它独特的螺旋式增量型开发过程中,测试随着项目的进展而不断积累。另外,由于强调整个开发小组拥有代码,测试也是由大家共同维护的。即,任何人在往代码库中放程序前,都应该运行一遍所有的测试;任何人如果发现了一个 BUG,都应该立即为这个 BUG 增加一个测试,而不是等待写那个程序的人来完成;任何人接手其他人的任务,或者修改其他人的代码和设计,改动完以后如果能通过所有测试,就证明他的工作没有破坏原系统。这样,测试才能真正起到帮助获得反馈的作用;而且,通过不断地优先编写和累积,测试应该可以基本覆盖全部的客户和开发需求,因此开发人员和客户可以得到尽可能充足的反馈。

        测试驱动开发(Test- Driven Development), 简称TDD, 由Kent Beck 提出的一种软件开发方式。测试驱动开发以测试作为开发过程的中心, 它要求在编写任何产品代码之前, 首先编写用于定义产品代码行为的测试, 而编写的产品代码又要以使测试通过为目标。测试驱动开发要求测试可以完全自动化的运行, 在对代码进行重构前后必须运行测试。测试驱动开发主要包括两方面:测试先行和代码重构。测试主要针对单元(最小的可测试软件元素)实施测试。它所测试的内容包括内部结构(如逻辑和数据流)以及单元的功能和可观测的行为。测试先行一改传统开发模式的单元测试在编写代码之后进行, 而将单元测试的编写移至编写正式代码之前。重构是在不改变代码外在行为的条件下改进其内部的行为的一种软件系统改变的过程, 使代码松耦合度(对外界代码依赖低)并且内聚度高(内部只完成一项功能) 。测试驱动开发作为极限 编程思想的一种主要实践, 可以有效地让程序开发人员开发出更高品质的、经过完整测试的程序。测试驱动开发以测试作为开发过程的开端, 它要求在编写任何产品代码之前,首先编写用于定义产品代码行为的测试, 而编写的产品代码又要以使测试通过为目标。TDD 不是一种开发工具,也不是一种测试方法, 它是一种编码之前进行单元测试的软件开发思想。

     

    1. 测试驱动开发的研究与实践

    测试驱动开发流程:TDD 开发过程有别于传统开发流程(Waterfall), 它在进行简单的概要设计后, 首先进行的是测试用例的编写,然后执行测试用例进行测试。测试失败, 则进行编码驱使测试通过, 这就是所谓的测试驱动。最终, 测试得到通过,再对代码进行重构,优化代码结构和性能。而传统流程则先进行概要设计, 然后在概要设计基础上进行详细设计,在详细设计阶段尽可能设想到全部问题和需求的解决方法, 然后才开始编码实现详细设计。TDD 开发流程图如图所示。

    测试驱动开发模式:在测试驱动开发中,关键问题如下:什么时候进行测试,如何选择要测试的逻辑和如何选择要测试的数据。测试驱动开发模式指导程序员如何解决上述问题。

    *测试相互独立。

    在测试驱动开发中, 所运行的各种测试之间关系的期望状态是没有任何相互影响的。相互独立的测试意味着所有的测试都是不依赖于顺序的, 可以随便从这些测试中挑出部分测试来运行。程序员必须将自己的问题分解为一些彼此正交的小问题, 这样就使得为每个测试搭建环境

    简单而快捷。独立测试鼓励利用高度内聚、低度耦合的对象组合来解决问题。

    *写出测试列表。

    程序员在开始写测试之前, 应该写一个包含所有必须要编写的测试的清单。那么, 记录到列表上的就是当前程序员要去实现的测试。首先将需要实现的每种操作的范例都记录在清单上。对于目前尚不存在的操作, 将其空版本记录在清单上。

    *测试和断言优先。

    在测试驱动开发中, 程序员构建一个系统应该是从其对最终系统的描述开始的。程序员应该从希望最终代码能够通过的测试开始编写一项功能。相应地,程序员应该从测试完成时能够通过的断言开始编写一个测试。在测试优先的测试里程序员应该尽量使用容易让人理解的数据, 一般不用一个常量来表达多种意思。

    一般从测试列表中选择具有指导意义并且比较有把握实现的测试来进行编写。当使用一个新类里的一种新的方法时, 不直接用它来编写程序, 而是编写一个小测试来验证这个API 的工作是否符合人们的愿望。当出现某种与当前讨论话题并不直接相关的想法时, 那么就在列表中增加一个测试然后重新回到论题上来。当发现一个错误的时候, 首先写一个尽可能小的测试并使其运行, 然后再去修复这个错误。

    利用JUnit进行测试驱动开发:在Eclipse 建立JUnit 测试, 并进行驱动开发。现在开 发一个"Hello Wor ld" 的例子。按照TDD 的规则, 应该在代码建立以前先把测试写好。为了能够在某处开始, 假设未来的类名是HelloWorld , 并且有一个方法Say(), 这个方法返回String 的值(例如"Hello World !")。根据设定的程序功能, 写出测试代码如下:

     

    import junit .framework.T estCase ;

    public class TestThatWeGetHelloWorldPrompt

    extends TestCase {

     public TestThatWeGetHelloWorldPrompt(

      String name){

       super(name);

      }

     public void testSay(){

     HelloWorld hi=new HelloWorld();

      assertEquals(" Hello World!" , hi.say());

     }

     public static void main(String[ ] args){

      junit.textui.TestRunner.run(

      TestThatWeGetHelloWorldPrompt.class);

     }

    }

    建立测试案例的步骤如下:

    1)建立一个junit.framework.TestCase的实例。

    2)定义一些以"test" 开头的无返回方法(例如test-WasTransactionSuccessful(), testS how(), 等等)。

    TestThatWeGetHelloWorldPrompt .java 包含这些:TestCase 的子类和一个叫做testSay()的方法。这个方法调用了assertEquals()函数, 它用来比较预期的值和由say()返回的值。main()方法用来运行测试和显示输出。JUnit 的TestRunner 处理测试, 提供基于图像和文本的输出表现形式。我们使用基于文本的版本, 因为Eclipse 支持它, 且也适合我们。当开始运行后, 基于文本的版本测试会以文本形式输出,Eclipse 会把这些输出自动变成图像界面的输出。

    现在建立被测试代码:

    public class HelloWorld {

     public String say(){

      return("Hello World!");

     }

    }

     

    1. 测试驱动开发在Java语言中的运用

    测试驱动开发与Java结合的实践应用:很多同学在程序开发过程中对测试不够重视,主要表现为:第一,没有针对实际问题设立出足够全面的测试用例;第二,主管认为某些代码是正确的,实际上无法为这些代码设计相应的测试用例。将测试驱动开发与Java课程有机结合起来,能够有效地客服同学们对测试认识的不足,从而编写出更高质量的程序。经过实践,可遵循以下步骤:

    首先编写测试用例:测试用例是对实际需求的现。用户向程序输入一个测试用例,则期望程序给出某些输出。输入和输出往往表现为函数关系。在传统的编程方式中,程序员也会考虑到实际需求问题,但往往由于仅限于思维上的没有做出清晰归纳的印象。尤其是一些心急的程序员,觉得写代码就是解决问题的全部工程。实际上这样忽略测试用例而编写出来的程序很容易未能覆盖实际问题的各方面要求。

    编写仅能通过测试用的代码:在编写测试用例阶段,首先要遵循的是:让编译器告知程序员,合适该增加新的方法,而不是主动对程序做出规划。在实际的Java语言课程教学实践中发现,有许多同学在针对某测试用例编写通过代码时,喜欢即兴发挥,写出一些并非通过本测试用例所需要的代码。这样可能会导致几个方面的问题:第一,这些代码无合适的测试用例验证,可能永远都不会被执行;第二,这些代码存在错误,但由于当前测试用例不是用于检验这些代码的,它们可能会遗漏到后期的软件开发中,甚至被集成到其它软件,造成后期难以检测出来的隐患;第三,这些代码可以解决问题的某一方面,但放置位置不合适,造成代

    码意义模糊。例如在上面的代码中,有些同学在完成了当前测试用例后,马上想到对于以文本文件方式输入的同类型的数据也可以使用同样的程序代码处理,于是在方法中加入有关文本文件的代码,这样的做法其实不合适,因为有关文本文件方式输入的处理应该在求最大公约数之前的方法中实现。

    代码重构:代码重构对于测试驱动开发来说相当重要。当程序中代码的代码越来越多,就有可能需要进行重构,以优化程序性能,并使程序更加"优美"。例如代码出现重复、某些代码要表达的意图过于复杂时,程序员都要考虑进行重构的必要性。代码重构与代码编写是交错进行的。代码编写是进行代码重构的基础,而适时地进行代码重构将能极大地加快代码编写的进度。另一方面,代码重构无可避免地增加了代码编写阶段的工作量,所以在进行重构的时机和范围都需要根据实际问题

    维护程序员测试集。进行增量式开发:对于测试集的编写,则应当遵循"不遗漏,不重复"的原则。通过前三个步骤,不断扩充测试用例至测试集,并立刻针对扩充的测试用例编写能通过的代码,直至测试集包括了实际问题的所有方面为止,则认为程序开发的代码编写阶段已经完成。

     

    1. 测试驱动开发在J2EE项目中实践

    J2EE是一种用来开发企业级软件应用系统的平台。目前针对J2EE项目,多采用多层架构设计,清晰简单、分工合作,每层使用特定的框架实现相应的设计策略。比如,Strum+Sprig+Hibernate就是一个很受欢迎的开源整合框架:表示层用Struts实现,业务层用Spring实现,持久层则用Hibernate实现。

    在这个整合框架下进行开发,对于普通水准的J2EE开发者来说,可能会有太多的时间被浪费在非关键任务上,或者是仅仅为了执行单元测试就不得不把所有程序部署到应用服务器上,其生产率是无法令人满意的。于是,一种全新的软件开发方法(测试驱动开发)就被提出且日益流行起来。

    JUnit工具介绍:作为流行成熟的回归测试框架,JUIlit提供了基于API的自动测试方法,您可以在测试代码中调用这个框架来检查条件是否满足,并报告错误的数量和类型。这种方案非常灵活,大多数情况下它大大减少了测试代码的维护时间,并且使应用中的复杂功能测试成为可能,还可交替使用白盒测试或黑盒测试。本文所讨论的测试工具有JMock、SpringContextTestCase、StrutsTestCase、Canoo WebTest,也都是JUnit在具体领域的扩展。

    项目组织结构:本项目采用的分层架构使Web应用达到了松散耦合还能灵活改变,并可以承载各种级别的业务处理,每层之间开放通信接口。各层都采用了测试先行编程的开发方法。

     

     

    src/dao(数据访问对象)目录存放持久层和域模型的实现;src/service目录存放业务层的实现;src/web目录存放表示层的实现。每层的测试用例则相应地存放在test目录下。

    Member类有四个属性,部分代码如下:

    持久层测试:

    该层测试使用了Spring对JUnit的扩展测试框架,AbstractTransactionalDataSourceSpringContextTests类的继承类MemberDaoTest可以不必依赖服务器而直接运行,从而对集成测试(类似单元测试)提供了良好的支持可以随意更新表中的数据而不必担心造成影响,因Spring的测试类支持事务管理,会自动圆滚在测试中所做的任何修改。按照TDD的骤接下来才是编写程序代码 ,创建MemberDao接口和相应的实现类MemberDao

    -Hibernate,然后在Spring配置文件中绑定。现在可以直接在Junit中进行测试。若测试通过,转到业务层开发。

    业务层测试: 我们使用的测试工具是JMock,它可以灵活地定义对象间交互时的约束关系,减少测试的脆弱性。

    使用JMock,需要继承MockObjeetTestCase类,先建立测试运行的上下文,然后设定Mock对象使用到方法、参数、返回值等行为,最后执行测试。按照测试先行方法,现在应该声明MemberManager接口并实现MemberManagerImpl类。

    表示层测试:Struts框架基于MVC(模型一视图一控制)设计模式,将数据访问、页面显示、流程逻辑三部分分离开来。这使得Web应用的容器内功能测试和单元测试变得困难。StrutsTestCase正是为测试Struts Web应用而创建的Mock测试框架,它无需启动Servlet容器就可以方便地测试.接下来,是创建MemberAction类、MemberForm类、memberForm页和memberList页的时候;然后开始该层测试。

     

    1. 在设计模式中的应用

    在传统的软件工程中,软件开发过程讲究的是前期详尽的需求和系统详细设计,以便开发出软件系统尽量与实际一致,但是这样做有一个缺点,容易造成 " 设计过度"。所谓设计过度,就是在尚未完全理解客户需求的基础上,就根据自己的理解做详细设计,并力求把系统设计得完美灵活。 然而一旦客户不需要那些功能或是改变需求的话, 就会造成开发过程中极大的浪费。

    测试驱动开发 ( test-driven development,TDD)是一种新型的程序开发方式,而不是一种测试方法,它是由 Kent Beck、Devid Astels 等人提出的,与传统的程序设计方法不同的软件开发方式,其基本思想是首先编写测试代码, 由测试来决定要编写哪些程序代码。TDD 的缺点是在前期过少的考虑整个系统架构,过多的强调了先测试后编码的原则,导致后面增加了重构的难度。

    设计模式[4]是在软件设计过程中解决某一类问题的方法。设计模式的基本思想是:根据系统的需求,在经过前人总结得出的方法中选出一种最适合当前系统使用的方法。 使用设计模式的好处是, 它是在无数前人经验的基础上总结得出的一些最本质的设计方法, 使用设计模式可以缩短系统结构设计的时间,能够保证系统的健壮性、扩展性和可维护性。 因此,设计模式是一种指导,在它的指导下,不仅有助于完成任务,而且有助于得到解决问题的最佳办法,从而做出一个优良的设计方案以达到事半功倍的效果。

    为了弥补TDD前期开发对系统结构设计不足的缺点,将TDD与设计模式结合进行软件设计则是一种新型开发方法。

    TDD的基本步骤如下:

    步骤 1 先写一段单元测试代码;
    步骤 2 执行这个测试代码,不能通过编译;
    步骤 3 写所能想到的最简单的程序代码,让单元测试代码可以通过编译;
    步骤 4 再次执行这个单元测试,应该会得到验证失败的结果 ( 如果通过的话,说明单元测试没有击中要害,需要修改测试代码);
    步骤 5 写主程序,让单元测试可以顺利通过验证;
    步骤 6 回到步骤 1,开始写新的单元测试。

     

    软件工程中无数项目的成功经验证明, 设计模式是非常重要和必要的。 对于初步接触

    TDD 的编程人员来说,结合设计模式进行的测试驱动开发最合理的方式应该是:① 花一定的时间做好前期的分析,在研究模式上投入时间;② 在最初以最简单的形式实现模式,以后再增加其复杂性;③ 如果使用了一种模式,而在以后发现无法满足需要时,通过转换的方式将其修改。 使用了结合设计模式进行的 TDD,将会极大的减少修改构架的复杂度,使得修改朝着有序的方向进行。

    结合设计模式的TDD的开发流程基本如下:

    可以看到,设计模式主要是用于业务逻辑层中。 业务逻辑层的作用就是处理系统中的各个业务逻辑,并将所得结果通过接口提供给外部的展现层调用。在开发过程中,遇到需要系统构架调整的时候,只要保持对应用程序的展示层的接口方法不变,无论下面层次中的程序做如何大的改动,前台都不需要做相应的变动,实现了展示层同业务逻辑层相分离的原则。这就是在 TDD 中也要使用设计模式的好处。

     

     

     

    1. 基于MVC的测试驱动开发

    基于 MVC 架构的测试驱动开发过程:由于具有使 View、Controller 与 Model 分离开来的特性,MVC 很适用于 GUI 软件的开发。目前许多 GUI 软件的结构都是基于或者是部分地基于MVC 的。比如,Microsoft Foundation Class(MFC)-----它把 View 和 Controller 集成在视图内,文档负责数据的表示以及存取(Model) ,视图负责显示文档内容(View)以及处理用户界面上的操作( Controller);Struts-----它采用EJB 作为模型,JSP 和 Custom Tag Library 配合作为View,Servlet作为控制器。

    根据对 MVC 架构各层的特点的分析,三层中 Controller 反映了应用程序的功能和流程,并且清楚 Model 和 View 的功能,所以测试驱动开发应该从Controller 出发,首先将开发的重点放在实现程序的功能上,更早地实现需求。由于在 Controller 的开发过程中需要不断地对 Model 和 View 进行重构,为了减少重构的代价,可以将 Model 中未实现的对象用 Mock Object 来代替。当在实现一个用户故事的 Controller 后,或者实现多个 Controller 后,从 Controller 层可以提取出 View 与 Controller 之间数据传递的信息,根据这些信息可以进行View 层的设计和开发,View 的实现主要考虑以怎样的视图来显示这些信息,以及设计怎样的事件来触发Controller中相应的方法。另一方面,当Controller 中可以至少提取出一个完整的 Model 对象时,调用 Model 层代码生成工具生成Model 的程序代码和测试代码。

    测试驱动开发中 MVC架构各部分的关系:MVC 架构的形式化分析,可知 MVC 三层模块之间是相互依赖的关系,而三层中 Controller 层反映了程序的控制逻辑,它的实现依赖于 Model向数据库获取数据,又依赖于 View 将更新的数据显示在界面上。所以在测试驱动开发 Controller 过程中可以设计出 Model 和 View 的接口。View 只是用开发环境提供的各种控件将数据简单的显示在界面上。Model 主要是对数据进行处理,数据处理的过程有其规律性,在绝大部分系统中,这些对象的方法主要是对数据库的操作,包括增加、删除、查询、修改等。三者的对应关系如下图。测试代码传入测试数据对被测代码的 public方法进行测试,随着 Controller 功能的实现,在 Controller 的程序代码部分可以知道,Model 和 View 对象的接口信息逐渐被设计和编写出来。对于 View 来说,它只负责显示图形用户界面,不涉及任何的功能代码,所以 View 层不适合作测试驱动开发,而是将 View 的测试集中在用户界面风格上,比如用户界面的一致性、界面的布局、用户界面之间的切换是否顺畅、颜色的使用是否适当、以及界面的设计是否考虑不同用户的需求等等。在 Web 界面测试方面,配合使用ASPUnit 和 HttpUnit 等自动化测试工具可以提高测试效率。Model 的测试集中在每个对象的方法是否返回正确的结果,Model 的方法确定了,对方法的测试用例也可以确定下来,而测试数据可以从 Controller 测试代码中用于测试的数据中获得。为了提供程序的编写效率,可以先从 Controller 的测试代码和程序代码提取出生成 Model 代码所需的信息,再利用代码生成工具,生成 Model 的测试代码和程序代码。

     

     

    Model信息的提取

    对测试驱动开发 Controller的基本约定

    对 Controller 的开发基本按照测试驱动开发的步骤来进行,将开发的重点放在实现程序的功能上。但是,为了能更好的实现从 Controller 层抽取出 Model和 View 层信息,并保证 Controller 层的可测试性,在开发 Controller 层时,还应该做到以下几点:

    1. 保持 View 的简单性,将事件代码交给 Controller 处理。因为测试 View是困难和繁琐的, 所以View应该是尽可能的简单。View中各控件的event handle事件只是传递的作用,具体的功能较由 Controller 的方法处理。

    2. MVC 三层的通信都要经过 Controller 层。这种限制保证了 Model 和 View 的分离,同时也保证了 Controller 层的测试用例可以完全覆盖整个程序的功能。

    3.为了便于用程序实现在 Controller 中提取自动生成 Model 的信息,Controller 代码中的某些方法和属性的命名应遵循一定的规则,具体如下:

    1) Model 方法的命名:方法类型+Model 类名,如 InsertUser。

    2) Model 几个常用方法的类型为:Insert 表示"插入",Search 表示"查询",Get 表示"单个查询",Update 表示"更新",Delete 表示"删除"。

    3)Controller 测试程序中的一个测试类对应一个 Controller 类,一个测试方法对应一个 Controller 方法的一个测试用例。

     

    1. 嵌入式系统测试驱动开发的策略

    双目标开发策略:对多数嵌入式项目来讲,并行进行硬件和软件开发是个现实。如果只能在目标硬件上运行,有可能会有多个浪费时间的因素。但并不是所有的开发团队都会遇到浪费时间的问题,传统意义上,这也是嵌入式开发者会转而使用评估板来缓解目标硬件的一个原因。评估板是在开发时使用的一种电路板,它有同目标系统相同的处理器配置,理想情况下还有同样的输入输出接口。评估板能保护不会延迟项目,但是这还不够,评估板仍然有构建时间长的问题。双目标开发则是解决上述瓶颈问题的一个策略。双目标是指代码被设计成至少应能在两个平台上运行:代码最终是要在一个嵌入式目标硬件上运行,但它首先在开发系统中写出和测试的,而双目标解决了以下几个问题:它可以在硬件就绪之前就测试代码,并使它在整个软件开发周期里避免硬件带来的瓶颈,还可避免随硬件和软件同时调试带来的互相指责。双目标还会影响设计、对软件与硬件之间边界的关注会产生更模块化的设计。在开发系统中,测试代码会在把代码应用于目标硬件之前来帮助开发人员建立信心,但在双目标方案当中存在其固有的风险。所以这些可能导致在一个环境里运行没有错误的代码却在另一个环境里却测试失败。在嵌入式中的TDD循环可以较好地应对这些问题。

    嵌入式的TDD循环:嵌入式的TDD循环是对TDD微循环的扩展,它可以克服目标硬件所带来的瓶颈。当构建和测试的循环只有几秒钟的情况下TDD效果最好,更长的构建和测试时间会导致采用更大的步伐。对这种快速反馈的需求迫使TDD的微循环脱离目标硬件,而运行在本地的开发系统中。TDD微循环是嵌人式TDD循环的第一个平台,如图所示。

     

     

     

    平台2~4被设计用于缓解用开发平台来运行单元测试所带来的风险。平台5确保完整集成后的系统能够提供可工作的特性。

    平台1:TDD微循环。在这个平台运行得最频繁,通常几分钟一次。大部分代码会在这个平台中写出,并且只在开发系统本地编译。测试是在开发系统里完成的,这样它能给出快速反馈,而不会被硬件的可靠性或可用性的约束拖累。在这个平台中,需要写于平台无关的代码。要寻找把软件和硬件断开的机会,硬件和软件的边界要很清楚,并记录在测试用例中。

    平台2:编译器兼容性检查。要定期的为目标系统作编译,采用为产品而使用的交叉编译器。这个平台是对编译器不兼容的一个早期警告。它会警告移植问题,如头文件不可用,语言支持不兼容,以及语言特性缺失等,不必在每次代码改变时都运行平台2。在每次采用了新的语言特性时做一下目标系统的交叉编译。

    平台3:在评估板上运行单元测试。有一个风险是,编译后的代码在本地开发系统和目标处理器上运行起来是不同的。为缓和这种风险,可以在评估板上运行单元测试。使用评估板可以看到代码在开发系统和目标处理器上行为的差异。拥有在评估板上运行的能力,即使在目标硬件就绪之后可能仍然比较方便。如果有一可疑的目标硬件行为,可以快速地通过在评估板上运行单元测试,以把目标硬件的问题包含进来或排出。

    平台4:在目标硬件上运行单元测试。平台4的目的和平台3相同,只是平台4会使用真实的内存。而且可以运行只能在目标硬件上运行的测试。这些测试可以识别出或者学习到目标硬件的行为。这个平台一个新增的功能是目标硬件上有限的内存。在这种情况下,可以把测试组织成不同的测试套件,使每个套件都能装进内存中。

    平台5:在目标硬件上运行验收测试。最后,需要在目标硬件上运行自动化的和手工的验收测试来保证产品特性。这里要确保任何不能完全被自动化测试的、依赖于硬件的代码都会被手工测试。

     

     

    [1] Badreddin O, Forward A, Lethbridge T C. A test-driven approach for developing software languages[C]// International Conference on Model-Driven Engineering and Software Development. IEEE, 2014:225-234.

    [2] 苏庆.SU Qing 测试驱动开发在Java语言课程实践中的应用[期刊论文]-广东工业大学学报(社会科学版) 2008(z1)

    [3] 程烨.高建华.CHENG Ye.GAO Jian-hua与设计模式相结合的测试驱动开发方法[期刊论文]-计算机工程与设计 2006(16)

    [4]Pipka J U. Test-Driven Web Application Development in Java[C]// Revised Papers from the International Conference NetObjectDays on Objects, Components, Architectures, Services, and Applications for a Networked World. Springer-Verlag, 2002:378-393.

    [5] Kent Beck. Test-Driven Development—By Example[J]. Pearson Schweiz Ag, 2003.

    [6] 黎利 基于MVC的测试驱动开发研究[学位论文]硕士 2007

    [7] 陈立群.CHEN Li-qun 测试驱动开发在J2EE项目中的全程实践[期刊论文]-计算机工程与科学2008(4)

    [8] 张扬.黄厚宽.ZHANG Yang.HUANG Hou-kuan 测试驱动开发及开发实践[期刊论文]-计算机技术与发展 2006(5)

    [9] 齐山松.姬进.QI Shansong.JI Jin测试驱动的嵌入式开发与实践[期刊论文]-电子科技 2013(8)

     

     

     

    展开全文
  • 测试驱动开发

    2017-04-20 21:22:55
    测试驱动开发,测试用例指导开发?开发结果必须有测试用例?
  • Python测试驱动开发(TDD)

    千次阅读 2018-08-16 22:56:02
    Python测试驱动开发(TDD) 前言:TDD是一种敏捷开发模式,而不是测试方法。 目录 Python测试驱动开发(TDD) 目录 单元测试与功能测试的区别 “单元测试/编写代码“循环 遵守不测试常量规则 有用的TDD概念 ...
  • 测试驱动开发的一些理解

    千次阅读 2017-08-02 23:30:29
    测试驱动开发的一些理解测试驱动开发解决什么问题? 系统测试和集成测试不容易覆盖一些代码细节,难以做到很高的代码覆盖率;测试驱动开发编写的测试一般是单元测试,而且由开发者编写,针对单个模块容易做到各个...
  • TDD、BDD、ATDD、DDD 软件开发模式

    万次阅读 2017-04-17 15:50:37
    TDD:测试驱动开发(Test-Driven Development) BDD:行为驱动开发(Behavior Driven Development) ATDD:验收测试驱动开发(Acceptance Test Driven Development) DDD:领域驱动开发(Domain Drive Design) ...
  • 浅谈TDD、BDD与ATDD软件开发

    万次阅读 2014-03-25 09:16:21
    TDD:测试驱动开发(Test-Driven Development) 测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。...
  • 初次接触自动化测试时,对数据驱动和关键字驱动不甚理解,觉得有点故弄玄须,不就是参数和函数嘛!其实其也体现了测试所不同与开发的一些特点(主要指系统测试),以及和对技术发展的脉络的展现。 1.录制/回放的...
  • 软件测试自动化的纠结

    万次阅读 热门讨论 2010-09-26 23:47:00
    在自动化测试过程中,从个人职业发展角度看,测试人员乐意采用原生态的脚本语言(如Python、VBScript,甚至C#),而从公司角度看,采用关键字驱动脚本的开发模式,这样就形成一对矛盾,让人们纠结。
  • 三种TDD开发模式

    千次阅读 2017-02-08 13:12:11
    首先了解一下这三个开发模式都是什么意思: TDD:测试驱动开发(Test-Driven Development) ...TDD的基本思路就是通过测试来推动整个开发的进行,但测试驱动开发并不只是单纯的测试工作,而是把需
  • TDD (test driver development)测试驱动开发

    万次阅读 2020-07-30 18:36:16
    为什么需要测试驱动/或者说需要单元测试我们工作接触的软件项目,不是学生时代,玩一玩就不管了... 如果你遇到这种情况,那么你急需要测试驱动这种软件开发方法。 测试驱动能够帮助我们,更加安全,自信的对代码进行修
  • 分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!...在JUnit的作者Kent Beck的大作《测试驱动开发:实战与模式解析》(Test-Driven Development: by Example)一书中有这么一段内容:...
  • 敏捷开发 —— TDD(测试驱动开发

    千次阅读 2018-06-06 22:45:43
    测试驱动开发 TDD(Test-Driven Development)是敏捷开发的一项核心实践,同时也是一种设计技术和方法。1. 基本思想在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷...
  • TDD:测试驱动开发(Test-Driven Development) 测试驱动开发是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码...
  • Win10下VS2015(WDK10)驱动开发环境配置

    万次阅读 多人点赞 2016-03-11 18:00:07
    微软在”WDK7600“以后就不再提供独立的内核驱动开发包了,而是必须首先安装微软集成开发环境VisualStudio,然后再从微软官网下载集成的WDK开发包、或者离线安装包,但是安装后Visual Studio就集驱动程序开发,编译...
  • 所谓的测试桩,就是你负责测试的模块/方法所调用的,所以你需要模仿他们做一个返回值(假的,但符合设计)。 桩的英文是stub;是指一个软件模块的框架或特殊目标实现,主要用于开发测试一个组件,该组件调用或...
1 2 3 4 5 ... 20
收藏数 296,735
精华内容 118,694
关键字:

测试驱动开发