2018-11-15 21:00:48 shanxixy 阅读数 763
  • Android底层技术:Linux驱动框架与开发

    于此,将框架(Framework)和设计模式(Design Pattern)应用于Linux驱动开发,说明了如何以面向对象、设计模式和框架概念来看待Linux驱动程序的架构。其直接的益处就是:让我们能基于一致的设计理念来结合Android HAL与Linux两层的驱动开发,规划出整体和谐Android-based软硬整合产品架构。

    22346 人正在学习 去看看 高煥堂

一、敏捷开发/测试的特征

      1. 敏捷开发提倡迭代式和增量式的开发模式,并强调测试在其中的重要性。

       敏捷开发模式的三个特点:依赖客户的参与、测试驱动以及紧凑的迭代开发周期。

      2. 敏捷测试是协同测试的一种形式,它要求每一个人都参与到测试计划的设计、实现和执行中去。客户通过定义用例以及程序属性参与到定义验收测试的设计中来。开发者和测试者共同打造可以进行功能自动化的测试配件。敏捷测试往往伴随着大量的沟通与协作工作。

      3. 敏捷开发下的测试与传统测试的不同点:

        1)意识从发现Bug转变为预防Bug出现,从越多发现Bug转变为越早发现Bug

        2)在测试前期:

          ①全程参与需求讨论,帮助需求和开发对需求有正确和共同的认识。如主导更多的用户场景、异常讨论等。

          ②测试的用例有优先级,针对性编写用例。

          ③在测试中,与开发直接交流、灵活应对变化,什么bug是重要的,什么可以后期做,分清楚Bug 的优先级。最高的优先级是“用户使用最多”以及“最容易发生bug”的场景交集。

          ④测试产出:测试用例、测试报告。

          ⑤缩短测试时间,更多使用自动化,如Selenium,fitness 的使用。

      4. 敏捷开发的两种常见模式

        1)TDD测试驱动开发

        TDD的基本思想就是在开发功能代码之前,先编写测试代码,然后只编写测试通过的代码,从而以测试来驱动整个开发过程的进行。这有助于编写简洁可用高质量的代码。

        2)BDD 行为驱动开发

        BDD是对TDD理念的扩展,TDD重点偏向开发,通过测试用例来规范约束开发者编写出质量更高、BUG更少的代码。而BDD更侧重设计,要求在设计测试用例时对系统进行定义,并用通用语言(如story形式)将系统的行为联系起来,将系统设计和测试用例结合,以此为驱动进行开发工作。

二、极限编程与测试

      极限编程模型是主流敏捷开发方法之一,这种轻量级的开发过程主要把目光集中于沟通、计划以及测试。极限编程中的测试成为“极限测试”。极限测试在极限编程中的地位非常重要,需要首先创建单元测试和验收测试,然后才能创建代码库。一旦代码库发生变更,就需要进行单元测试。在重要的发布结点,由客户来执行验收测试。

2014-09-29 22:47:31 CDUT100 阅读数 1244
  • Android底层技术:Linux驱动框架与开发

    于此,将框架(Framework)和设计模式(Design Pattern)应用于Linux驱动开发,说明了如何以面向对象、设计模式和框架概念来看待Linux驱动程序的架构。其直接的益处就是:让我们能基于一致的设计理念来结合Android HAL与Linux两层的驱动开发,规划出整体和谐Android-based软硬整合产品架构。

    22346 人正在学习 去看看 高煥堂

一、中文版

《敏捷软件开发:原则、模式与实践》 

《解析极限编程—拥抱变化》
《探索极限编程》
《敏捷迭代开发:管理者指南》
《敏捷建模:极限编程和统一过程的有效实践》
《敏捷项目管理》
《敏捷软件开发》
《超越传统的软件开发——极限编程的幻象与真实》
《测试驱动开发——实用指南》
《测试驱动开发(中文版)》
《极限编程实践》
《规划极限编程》
《极限编程实施》
《极限编程研究》

《特征驱动开发方法原理与实践》

二、英文版

1: Robert C. Martin
Agile Software Development: Principles, Patterns and Practices

2: Martin Fowler
Refactoring: Improving the Design of Existing Code

3: Mike Cohn
Agile Estimating and Planning

4: Mike Cohn
User Stories Applied: For Agile Software Development

5: Andrew Hunt , David Thomas
The Pragmatic Programmer: From Journeyman to Master

6: Alistair Cockburn
Agile Software Development: The Cooperative Game (2nd Edition)

7: Craig Larman
Agile and Iterative Development: A Manager's Guide

8: Kent Beck
Extreme Programming Explained: Embrace Change (2nd Edition)

9: Jim Highsmith
Agile Project Management: Creating Innovative Products

10: Paul Duvall, etc.
Continuous Integration: Improving Software Quality and Reducing Risk

11: Mary Poppendieck , Tom Poppendieck
Lean Software Development: An Agile Toolkit

12: Ken Schwaber
Agile Project Management with Scrum

13: Ken Schwaber, Mike Beedle
Agile Software Development with Scrum

14: Alistair Cockburn
Crystal Clear: A Human-Powered Methodology for Small Teams

15: Venkat Subramaniam, Andy Hunt
Practices of an Agile Developer: Working in the Real World

16: Kent Beck
Test Driven Development: By Example

17: Johanna Rothman 
Manage It!: Your Guide to Modern, Pragmatic Project Management

18: James Shore , Shane Warden
The Art of Agile Development

19: Ron Jeffries, etc.
Extreme Programming Installed

20: Esther Derby, etc.
Agile Retrospectives: Making Good Teams Great

2008-07-08 15:50:47 iteye_11232 阅读数 18
  • Android底层技术:Linux驱动框架与开发

    于此,将框架(Framework)和设计模式(Design Pattern)应用于Linux驱动开发,说明了如何以面向对象、设计模式和框架概念来看待Linux驱动程序的架构。其直接的益处就是:让我们能基于一致的设计理念来结合Android HAL与Linux两层的驱动开发,规划出整体和谐Android-based软硬整合产品架构。

    22346 人正在学习 去看看 高煥堂

这两个圆圈表示不同的视角上的敏捷实践,包括开发者视角和项目管理的视角。接下来从里向外进行介绍,因为有些实践我了解得不清楚,如果下面有哪些说得不对的地方也请大家指出。

Test-Driven Development,测试驱动开发,它是敏捷开发的最重要的部分。在ThoughtWorks,我们实现任何一个功能都是从测试开始,首先对业务需求进行分析,分解为一个一个的Story,记录在Story Card上。然后两个人同时坐在电脑前面,一个人依照Story,从业务需求的角度来编写测试代码,另一个人看着他并且进行思考,如果有不同的意见就会提出来进行讨论,直到达成共识,这样写出来的测试代码就真实反映了业务功能需求。接着由另一个人控制键盘,编写该测试代码的实现。如果没有测试代码,就不能编写功能的实现代码。先写测试代码,能够让开发人员明确目标,就是让测试通过。

Continuous Integration,持续集成。在以往的软件开发过程中,集成是一件很痛苦的事情,通常很长时间才会做一次集成,这样的话,会引发很多问题,比如build未通过或者单元测试失败。敏捷开发中提倡持续集成,一天之内集成十几次甚至几十次,如此频繁的集成能尽量减少冲突,由于集成很频繁,每一次集成的改变也很少,即使集成失败也容易定位错误。一次集成要做哪些事情呢?它至少包括:获得所有源代码;编译源代码;运行所有测试,包括单元测试、功能测试等;确认编译和测试是否通过,最后发送报告。当然也会做一些其它的任务,比如说代码分析、测试覆盖率分析等等。 在我们公司里,开发人员的桌上有一个火山灯用来标志集成的状态,如果是黄灯,表示正在集成;如果是绿灯,表示上一次集成通过,开发人员在这时候获得的代码是可用而可靠的;如果显示为红灯,就要小心了,上一次集成未通过,需要尽快定位失败原因从而让灯变绿。在持续集成上,我们公司使用的是自己开发的产品CruiseControlRefactoring,重构。相信大家对它都很熟悉了,有很多很多的书用来介绍重构,最著名的是Martin的《重构》,Joshua的《从重构到模式》等。重构是在不改变系统外部行为下,对内部结构进行整理优化,使得代码尽量简单、优美、可扩展。在以往开发中,通常是在有需求过来,现在的系统架构不容易实现,从而对原有系统进行重构;或者在开发过程中有剩余时间了,对现在代码进行重构整理。但是在敏捷开发中,重构贯穿于整个开发流程,每一次开发者check in代码之前,都要对所写代码进行重构,让代码达到clean code that works。值得注意的是,在重构时,每一次改变要尽可能小,用单元测试来保证重构是否引起冲突,并且不只是对实现代码进行重构,如果测试代码中有重复,也要对它进行重构。

Pair-Programming,结对编程。在敏捷开发中,做任何事情都是Pair的,包括分析、写测试、写实现代码或者重构。Pair做事有很多好处,两个人在一起探讨很容易产生思想的火花,也不容易走上偏路。在我们公司,还有很多事都是Pair来做,比如Pair学习,Pair翻译,Pair做PPT,关于这个话题,钱钱同学有一篇很有名的文章对它进行介绍,名为Pair Programming (结对编程)。

Stand up,站立会议。每天早上,项目组的所有成员都会站立进行一次会议,由于是站立的,所以时间不会很长,一般来说是15-20分钟。会议的内容并不是需求分析、任务分配等,而是每个人都回答三个问题:1. 你昨天做了什么?2. 你今天要做什么? 3. 你遇到了哪些困难?站立会议让团队进行交流,彼此相互熟悉工作内容,如果有人曾经遇到过和你类似的问题,那么在站立会议后,他就会和你进行讨论。

Frequent Releases,小版本发布。在敏捷开发中,不会出现这种情况,拿到需求以后就闭门造车,直到最后才将产品交付给客户,而是尽量多的产品发布,一般以周、月为单位。这样,客户每隔一段时间就会拿到发布的产品进行试用,而我们可以从客户那得到更多的反馈来改进产品。正因为发布频繁,每一个版本新增的功能简单,不需要复杂的设计,这样文档和设计就在很大程度上简化了。又因为简单设计,没有复杂的架构,所以客户有新的需求或者需求进行变动,也能很快的适应。

Minimal Documentation,较少的文档。其实敏捷开发中并不是没有文档,而是有大量的文档,即测试。这些测试代码真实的反应了客户的需求以及系统API的用法,如果有新人加入团队,最快的熟悉项目的方法就是给他看测试代码,而比一边看着文档一边进行debug要高效。如果用书面文档或者注释,某天代码变化了,需要对这些文档进行更新。一旦忘记更新文档,就会出现代码和文档不匹配的情况,这更加会让人迷惑。而在敏捷中并不会出现,因为只有测试变化了,代码才会变化,测试是真实反应代码的。 这时有人会问:代码不写注释行吗?一般来说好的代码不是需要大量的注释吗?其实简单可读的代码才是好的代码,既然简单可读了,别人一看就能够看懂,这时候根本不需要对代码进行任何注释。若你觉得这段代码不加注释的话别人可能看不懂,就表示设计还不够简单,需要对它进行重构。

Collaborative Focus,以合作为中心,表现为代码共享。在敏捷开发中,代码是归团队所有而不是哪些模块的代码属于哪些人,每个人都有权利获得系统任何一部分的代码然后修改它,如果有人看到某些代码不爽的话,那他能够对这部分代码重构而不需要征求代码作者的同意,很可能也不知道是谁写的这部分代码。这样每个人都能熟悉系统的代码,即使团队的人员变动,也没有风险。

Customer Engagement ,现场客户。敏捷开发中,客户是与开发团队一起工作的,团队到客户现场进行开发或者邀请客户到团队公司里来开发。如果开发过程中有什么问题或者产品经过一个迭代后,能够以最快速度得到客户的反馈。

Automated Testing ,自动化测试。为了减小人力或者重复劳动,所有的测试包括单元测试、功能测试或集成测试等都是自动化的,这对QA人员提出了更高的要求。他们要熟悉开发语言、自动化测试工具,能够编写自动化测试脚本或者用工具录制。我们公司在自动化测试上做了大量的工作,包括Selenium开源项目。

Adaptive Planning,可调整计划。敏捷开发中计划是可调整的,并不是像以往的开发过程中,需求分析->概要设计->详细设计->开发->测试->交付,每一个阶段都是有计划的进行,一个阶段结束便开始下一个阶段。而敏捷开发中只有一次一次的迭代,小版本的发布,根据客户反馈随时作出相应的调整和变化。

敏捷开发过程与传统的开发过程有很大不同,在这过程中,团队是有激情有活力的,能够适应更大的变化,做出更高质量的软件。

2008-10-12 15:31:52 zhangchibang311 阅读数 21
  • Android底层技术:Linux驱动框架与开发

    于此,将框架(Framework)和设计模式(Design Pattern)应用于Linux驱动开发,说明了如何以面向对象、设计模式和框架概念来看待Linux驱动程序的架构。其直接的益处就是:让我们能基于一致的设计理念来结合Android HAL与Linux两层的驱动开发,规划出整体和谐Android-based软硬整合产品架构。

    22346 人正在学习 去看看 高煥堂

{关键字}

测试驱动开发/Test Driven Development/TDD
测试用例/TestCase/TC
设计/Design
重构/Refactoring

{TDD的目标}

Clean Code That Works

这句话的含义是,事实上我们只做两件事情:让代码奏效(Work)和让代码洁净(Clean),前者是把事情做对,后者是把事情做好。想想看,其实 我们平时所做的所有工作,除去无用的工作和错误的工作以外,真正正确的工作,并且是真正有意义的工作,其实也就只有两大类:增加功能和提升设计,而TDD 正是在这个原则上产生的。如果您的工作并非我们想象的这样,(这意味着您还存在第三类正确有意义的工作,或者您所要做的根本和我们在说的是两回事),那么 这告诉我们您并不需要TDD,或者不适用TDD。而如果我们偶然猜对(这对于我来说是偶然,而对于Kent Beck和Martin Fowler这样的大师来说则是辛勤工作的成果),那么恭喜您,TDD有可能成为您显著提升工作效率的一件法宝。请不要将信将疑,若即若离,因为任何一项 新的技术——只要是从根本上改变人的行为方式的技术——就必然使得相信它的人越来越相信,不信的人越来越不信。这就好比学游泳,唯一能学会游泳的途径就是 亲自下去游,除此之外别无他法。这也好比成功学,即使把卡耐基或希尔博士的书倒背如流也不能拥有积极的心态,可当你以积极的心态去成就了一番事业之后,你 就再也离不开它了。相信我,TDD也是这样!想试用TDD的人们,请遵循下面的步骤:

编写TestCase --> 实现TestCase --> 重构
(确定范围和目标)   (增加功能)   (提升设计)

[友情提示:敏捷建模中的一个相当重要的实践被称为:Prove it With Code,这种想法和TDD不谋而合。]

{TDD的优点}

『充满吸引力的优点』

完工时完工。表明我可以很清楚的看到自己的这段工作已经结束了,而传统的方式很难知道什么时候编码工作结束了。 全面正确的认识代码和利用代码,而传统的方式没有这个机会。 为利用你成果的人提供Sample,无论它是要利用你的源代码,还是直接重用你提供的组件。 开发小组间降低了交流成本,提高了相互信赖程度。 避免了过渡设计。 系统可以与详尽的测试集一起发布,从而对程序的将来版本的修改和扩展提供方便。 TDD给了我们自信,让我们今天的问题今天解决,明天的问题明天解决,今天不能解决明天的问题,因为明天的问题还没有出现(没有TestCase),除非有TestCase否则我决不写任何代码;明天也不必担心今天的问题,只要我亮了绿灯。

『不显而易见的优点』

逃避了设计角色。对于一个敏捷的开发小组,每个人都在做设计。 大部分时间代码处在高质量状态,100%的时间里成果是可见的。 由于可以保证编写测试和编写代码的是相同的程序员,降低了理解代码所花费的成本。 为减少文档和代码之间存在的细微的差别和由这种差别所引入的Bug作出杰出贡献。 在预先设计和紧急设计之间建立一种平衡点,为你区分哪些设计该事先做、哪些设计该迭代时做提供了一个可靠的判断依据。

『有争议的优点』

事实上提高了开发效率。每一个正在使用TDD并相信TDD的人都会相信这一点,但观望者则不同,不相信TDD的人甚至坚决反对这一点,这很正常,世界总是这样。 发现比传统测试方式更多的Bug。 使IDE的调试功能失去意义,或者应该说,避免了令人头痛的调试和节约了调试的时间。 总是处在要么编程要么重构的状态下,不会使人抓狂。(两顶帽子) 单元测试非常有趣。

{TDD的步骤}

编写TestCase --> 实现TestCase --> 重构
(不可运行)   (可运行)   (重构)
步骤 制品
(1)快速新增一个测试用例 新的TestCase
(2)编译所有代码,刚刚写的那个测试很可能编译不通过 原始的TODO List
(3)做尽可能少的改动,让编译通过 Interface
(4)运行所有的测试,发现最新的测试不能编译通过 -(Red Bar)
(5)做尽可能少的改动,让测试通过 Implementation
(6)运行所有的测试,保证每个都能通过 -(Green Bar)
(7)重构代码,以消除重复设计 Clean Code That Works

{FAQ}

[什么时候重构?]
如果您在软件公司工作,就意味着您成天都会和想通过重构改善代码质量的想法打交道,不仅您如此,您的大部 分同事也都如此。可是,究竟什么时候该重构,什么情况下应该重构呢?我相信您和您的同事可能有很多不同的看法,最常见的答案是“该重构时重构”,“写不下 去的时候重构”,和“下一次迭代开始之前重构”,或者干脆就是“最近没时间,就不重构了,下次有时间的时候重构吧”。正如您已经预见到我想说的——这些想 法都是对重构的误解。重构不是一种构建软件的工具,不是一种设计软件的模式,也不是一个软件开发过程中的环节,正确理解重构的人应该把重构看成一种书写代 码的方式,或习惯,重构时时刻刻有可能发生。在TDD中,除去编写测试用例和实现测试用例之外的所有工作都是重构,所以,没有重构任何设计都不能实现。至 于什么时候重构嘛,还要分开看,有三句话是我的经验:实现测试用例时重构代码,完成某个特性时重构设计,产品的重构完成后还要记得重构一下测试用例哦。

[什么时候设计?]
这个问题比前面一个要难回答的多,实话实说,本人在依照TDD开发软件的时候也常常被这个问题困扰,总是 觉得有些问题应该在写测试用例之前定下来,而有些问题应该在新增一个一个测试用例的过程中自然出现,水到渠成。所以,我的建议是,设计的时机应该由开发者 自己把握,不要受到TDD方式的限制,但是,不需要事先确定的事一定不能事先确定,免得捆住了自己的手脚。

[什么时候增加新的TestCase?]
没事做的时候。通常我们认为,如果你要增加一个新的功能,那么先写一个不能通过的 TestCase;如果你发现了一个bug,那么先写一个不能通过的TestCase;如果你现在什么都没有,从0开始,请先写一个不能通过的 TestCase。所有的工作都是从一个TestCase开始。此外,还要注意的是,一些大师要求我们每次只允许有一个TestCase亮红灯,在这个 TestCase没有Green之前不可以写别的TestCase,这种要求可以适当考虑,但即使有多个TestCase亮红灯也不要紧,并未违反TDD 的主要精神。

[TestCase该怎么写?]
测试用例的编写实际上就是两个过程:使用尚不存在的代码和定义这些代码的执行结果。所以一个 TestCase也就应该包括两个部分——场景和断言。第一次写TestCase的人会有很大的不适应的感觉,因为你之前所写的所有东西都是在解决问题, 现在要你提出问题确实不大习惯,不过不用担心,你正在做正确的事情,而这个世界上最难的事情也不在于如何解决问题,而在于ask the right question!

[TDD能帮助我消除Bug吗?]
答:不能!千万不要把“测试”和“除虫”混为一谈!“除虫”是指程序员通过自己的努力来减 少bug的数量(消除bug这样的字眼我们还是不要讲为好^_^),而“测试”是指程序员书写产品以外的一段代码来确保产品能有效工作。虽然TDD所编写 的测试用例在一定程度上为寻找bug提供了依据,但事实上,按照TDD的方式进行的软件开发是不可能通过TDD再找到bug的(想想我们前面说的“完工时 完工”),你想啊,当我们的代码完成的时候,所有的测试用例都亮了绿灯,这时隐藏在代码中的bug一个都不会露出马脚来。

但是,如果要问“测试”和“除虫”之间有什么联系,我相信还是有很多话可以讲的,比如TDD事实上减少了bug的数量,把查找bug战役的关注点从 全线战场提升到代码战场以上。还有,bug的最可怕之处不在于隐藏之深,而在于满天遍野。如果你发现了一个用户很不容易才能发现的bug,那么不一定对工 作做出了什么杰出贡献,但是如果你发现一段代码中,bug的密度或离散程度过高,那么恭喜你,你应该抛弃并重写这段代码了。TDD避免了这种情况,所以将 寻找bug的工作降低到了一个新的低度。

[我该为一个Feature编写TestCase还是为一个类编写TestCase?]
初学者常问的问题。虽然我们从TDD 的说明书上看到应该为一个特性编写相应的TestCase,但为什么著名的TDD大师所写的TestCase都是和类/方法一一对应的呢?为了解释这个问 题,我和我的同事们都做了很多试验,最后我们得到了一个结论,虽然我不知道是否正确,但是如果您没有答案,可以姑且相信我们。

我们的研究结果表明,通常在一个特性的开发开始时,我们针对特性编写测试用例,如果您发现这个特性无法用TestCase表达,那么请将这个特性细 分,直至您可以为手上的特性写出TestCase为止。从这里开始是最安全的,它不会导致任何设计上重大的失误。但是,随着您不断的重构代码,不断的重构 TestCase,不断的依据TDD的思想做下去,最后当产品伴随测试用例集一起发布的时候,您就会不经意的发现经过重构以后的测试用例很可能是和产品中 的类/方法一一对应的。

[什么时候应该将全部测试都运行一遍?]
Good Question!大师们要求我们每次重构之后都要完整的运行一遍测试用例。这个要求可以理解,因为重构很可能会改变整个代码的结构或设计,从而导致不可 预见的后果,但是如果我正在开发的是一个ERP怎么办?运行一遍完整的测试用例可能将花费数个小时,况且现在很多重构都是由工具做到的,这个要求的可行性 和前提条件都有所动摇。所以我认为原则上你可以挑几个你觉得可能受到本次重构影响的TestCase去run,但是如果运行整个测试包只要花费数秒的时 间,那么不介意你按大师的要求去做。

[什么时候改进一个TestCase?]
增加的测试用例或重构以后的代码导致了原来的TestCase的失去了效果,变得无 意义,甚至可能导致错误的结果,这时是改进TestCase的最好时机。但是有时你会发现,这样做仅仅导致了原来的TestCase在设计上是臃肿的,或 者是冗余的,这都不要紧,只要它没有失效,你仍然不用去改进它。记住,TestCase不是你的产品,它不要好看,也不要怎么太科学,甚至没有性能要求, 它只要能完成它的使命就可以了——这也证明了我们后面所说的“用Ctrl-C/Ctrl-V编写测试用例”的可行性。

但是,美国人的想法其实跟我们还是不太一样,拿托尼巴赞的MindMap来说吧,其实画MindMap只是为了表现自己的思路,或记忆某些重要的事 情,但托尼却建议大家把MindMap画成一件艺术品,甚至还有很多艺术家把自己画的抽象派MindMap拿出来帮助托尼做宣传。同样,大师们也要求我们 把TestCase写的跟代码一样质量精良,可我想说的是,现在国内有几个公司能把产品的代码写的精良??还是一步一步慢慢来吧。

[为什么原来通过的测试用例现在不能通过了?]
这是一个警报,Red Alert!它可能表达了两层意思——都不是什么好意思——1)你刚刚进行的重构可能失败了,或存在一些错误未被发现,至少重构的结果和原来的代码不等价 了。2)你刚刚增加的TestCase所表达的意思跟前面已经有的TestCase相冲突,也就是说,新增的功能违背了已有的设计,这种情况大部分可能是 之前的设计错了。但无论哪错了,无论是那层意思,想找到这个问题的根源都比TDD的正常工作要难。

[我怎么知道那里该有一个方法还是该有一个类?]
这个问题也是常常出现在我的脑海中,无论你是第一次接触TDD或者已经成为 TDD专家,这个问题都会缠绕着你不放。不过问题的答案可以参考前面的“什么时候设计”一节,答案不是唯一的。其实多数时候你不必考虑未来,今天只做今天 的事,只要有重构工具,从方法到类和从类到方法都很容易。

[我要写一个TestCase,可是不知道从哪里开始?]
从最重要的事开始,what matters most?从脚下开始,从手头上的工作开始,从眼前的事开始。从一个没有UI的核心特性开始,从算法开始,或者从最有可能耽误时间的模块开始,从一个最严 重的bug开始。这是TDD主义者和鼠目寸光者的一个共同点,不同点是前者早已成竹在胸。

[为什么我的测试总是看起来有点愚蠢?]
哦?是吗?来,握个手,我的也是!不必担心这一点,事实上,大师们给的例子也相当愚 蠢,比如一个极端的例子是要写一个两个int变量相加的方法,大师先断言2+3=5,再断言5+5=10,难道这些代码不是很愚蠢吗?其实这只是一个极端 的例子,当你初次接触TDD时,写这样的代码没什么不好,以后当你熟练时就会发现这样写没必要了,要记住,谦虚是通往TDD的必经之路!从经典开发方法转 向TDD就像从面向过程转向面向对象一样困难,你可能什么都懂,但你写出来的类没有一个纯OO的!我的同事还告诉我真正的太极拳,其速度是很快的,不比任 何一个快拳要慢,但是初学者(通常是指学习太极拳的前10年)太不容易把每个姿势都做对,所以只能慢慢来。

[什么场合不适用TDD?]
问的好,确实有很多场合不适合使用TDD。比如对软件质量要求极高的军事或科研产品——神州六号,人命关天的软件——医疗设备,等等,再比如设计很重要必须提前做好的软件,这些都不适合TDD,但是不适合TDD不代表不能写TestCase,只是作用不同,地位不同罢了。

{Best Practise}

[微笑面对编译错误]
学生时代最害怕的就是编译错误,编译错误可能会被老师视为上课不认真听课的证据,或者同学间相互嘲笑的 砝码。甚至离开学校很多年的老程序员依然害怕它就像害怕迟到一样,潜意识里似乎编译错误极有可能和工资挂钩(或者和智商挂钩,反正都不是什么好事)。其 实,只要提交到版本管理的代码没有编译错误就可以了,不要担心自己手上的代码的编译错误,通常,编译错误都集中在下面三个方面:
(1)你的代码存在低级错误
(2)由于某些Interface的实现尚不存在,所以被测试代码无法编译
(3)由于某些代码尚不存在,所以测试代码无法编译
请注意第二点与第三点完全不同,前者表明设计已存在,而实现不存在导致的编译错误;后者则指仅有TestCase而其它什么都没有的情况,设计和实现都不存在,没有Interface也没有Implementation。

另外,编译器还有一个优点,那就是以最敏捷的身手告诉你,你的代码中有那些错误。当然如果你拥有Eclipse这样可以及时提示编译错误的IDE,就不需要这样的功能了。

[重视你的计划清单]
在非TDD的情况下,尤其是传统的瀑布模型的情况下,程序员不会不知道该做什么,事实上,总是有设计或 者别的什么制品在引导程序员开发。但是在TDD的情况下,这种优势没有了,所以一个计划清单对你来说十分重要,因为你必须自己发现该做什么。不同性格的人 对于这一点会有不同的反应,我相信平时做事没什么计划要依靠别人安排的人(所谓将才)可能略有不适应,不过不要紧,Tasks和Calendar(又称效 率手册)早已成为现代上班族的必备工具了;而平时工作生活就很有计划性的人,比如我:),就会更喜欢这种自己可以掌控Plan的方式了。

[废黜每日代码质量检查]
如果我没有记错的话,PSP对于个人代码检查的要求是蛮严格的,而同样是在针对个人的问题上, TDD却建议你废黜每日代码质量检查,别起疑心,因为你总是在做TestCase要求你做的事情,并且总是有办法(自动的)检查代码有没有做到这些事情 ——红灯停绿灯行,所以每日代码检查的时间可能被节省,对于一个严格的PSP实践者来说,这个成本还是很可观的!

此外,对于每日代码质量检查的另一个好处,就是帮助你认识自己的代码,全面的从宏观、微观、各个角度审视自己的成果,现在,当你依照TDD做事时,这个优点也不需要了,还记得前面说的TDD的第二个优点吗,因为你已经全面的使用了一遍你的代码,这完全可以达到目的。

但是,问题往往也并不那么简单,现在有没有人能告诉我,我如何全面审视我所写的测试用例呢?别忘了,它们也是以代码的形式存在的哦。呵呵,但愿这个 问题没有把你吓到,因为我相信到目前为止,它还不是瓶颈问题,况且在编写产品代码的时候你还是会自主的发现很多测试代码上的没考虑到的地方,可以就此修改 一下。道理就是如此,世界上没有任何方法能代替你思考的过程,所以也没有任何方法能阻止你犯错误,TDD仅能让你更容易发现这些错误而已。

[如果无法完成一个大的测试,就从最小的开始]
如果我无法开始怎么办,教科书上有个很好的例子:我要写一个电影列表的类,我 不知道如何下手,如何写测试用例,不要紧,首先想象静态的结果,如果我的电影列表刚刚建立呢,那么它应该是空的,OK,就写这个断言吧,断言一个刚刚初始 化的电影列表是空的。这不是愚蠢,这是细节,奥运会五项全能的金牌得主玛丽莲·金是这样说的:“成功人士的共同点在于……如果目标不够清晰,他们会首先做 通往成功道路上的每一个细小步骤……”。

[尝试编写自己的xUnit]
Kent Beck建议大家每当接触一个新的语言或开发平台的时候,就自己写这个语言或平台的xUnit,其实几乎所有常用的语言和平台都已经有了自己的 xUnit,而且都是大同小异,但是为什么大师给出了这样的建议呢。其实Kent Beck的意思是说通过这样的方式你可以很快的了解这个语言或平台的特性,而且xUnit确实很简单,只要知道原理很快就能写出来。这对于那些喜欢自己写 底层代码的人,或者喜欢控制力的人而言是个好消息。

[善于使用Ctrl-C/Ctrl-V来编写TestCase]
不必担心TestCase会有代码冗余的问题,让它冗余好了。

[永远都是功能First,改进可以稍后进行]
上面这个标题还可以改成另外一句话:避免过渡设计!

[淘汰陈旧的用例]
舍不得孩子套不着狼。不要可惜陈旧的用例,因为它们可能从概念上已经是错误的了,或仅仅会得出错误的结果,或者在某次重构之后失去了意义。当然也不一定非要删除它们,从TestSuite中除去(JUnit)或加上Ignored(NUnit)标签也是一个好办法。

[用TestCase做试验]
如果你在开始某个特性或产品的开发之前对某个领域不太熟悉或一无所知,或者对自己在该领域里的 能力一无所知,那么你一定会选择做试验,在有单元测试作工具的情况下,建议你用TestCase做试验,这看起来就像你在写一个验证功能是否实现的 TestCase一样,而事实上也一样,只不过你所验证的不是代码本身,而是这些代码所依赖的环境。

[TestCase之间应该尽量独立]
保证单独运行一个TestCase是有意义的。

[不仅测试必须要通过的代码,还要测试必须不能通过的代码]
这是一个小技巧,也是不同于设计思路的东西。像越界的值或者乱 码,或者类型不符的变量,这些输入都可能会导致某个异常的抛出,或者导致一个标示“illegal parameters”的返回值,这两种情况你都应该测试。当然我们无法枚举所有错误的输入或外部环境,这就像我们无法枚举所有正确的输入和外部环境一 样,只要TestCase能说明问题就可以了。

[编写代码的第一步,是在TestCase中用Ctrl-C]
这是一个高级技巧,呃,是的,我是这个意思,我不是说这个技巧 难以掌握,而是说这个技巧当且仅当你已经是一个TDD高手时,你才能体会到它的魅力。多次使用TDD的人都有这样的体会,既然我的TestCase已经写 的很好了,很能说明问题,为什么我的代码不能从TestCase拷贝一些东西来呢。当然,这要求你的TestCase已经具有很好的表达能力,比如断言f (5)=125的方式显然没有断言f(5)=5^(5-2)表达更多的内容。

[测试用例包应该尽量设计成可以自动运行的]
如果产品是需要交付源代码的,那我们应该允许用户对代码进行修改或扩充后在自己 的环境下run整个测试用例包。既然通常情况下的产品是可以自动运行的,那为什么同样作为交付用户的制品,测试用例包就不是自动运行的呢?即使产品不需要 交付源代码,测试用例包也应该设计成可以自动运行的,这为测试部门或下一版本的开发人员提供了极大的便利。

[只亮一盏红灯]
大师的建议,前面已经提到了,仅仅是建议。

[用TestCase描述你发现的bug]
如果你在另一个部门的同事使用了你的代码,并且,他发现了一个bug,你猜他会怎 么做?他会立即走到你的工位边上,大声斥责说:“你有bug!”吗?如果他胆敢这样对你,对不起,你一定要冷静下来,不要当面回骂他,相反你可以微微一 笑,然后心平气和的对他说:“哦,是吗?那么好吧,给我一个TestCase证明一下。”现在局势已经倒向你这一边了,如果他还没有准备好回答你这致命的 一击,我猜他会感到非常羞愧,并在内心责怪自己太莽撞。事实上,如果他的TestCase没有过多的要求你的代码(而是按你们事前的契约),并且亮了红 灯,那么就可以确定是你的bug,反之,对方则无理了。用TestCase描述bug的另一个好处是,不会因为以后的修改而再次暴露这个bug,它已经成 为你发布每一个版本之前所必须检查的内容了。

{关于单元测试}

单元测试的目标是

Keep the bar green to keep the code clean

这句话的含义是,事实上我们只做两件事情:让代码奏效(Keep the bar green)和让代码洁净(Keep the code clean),前者是把事情做对,后者是把事情做好,两者既是TDD中的两顶帽子,又是xUnit架构中的因果关系。

单元测试作为软件测试的一个类别,并非是xUnit架构创造的,而是很早就有了。但是xUnit架构使得单元测试变得直接、简单、高效和规范,这也 是单元测试最近几年飞速发展成为衡量一个开发工具和环境的主要指标之一的原因。正如Martin Fowler所说:“软件工程有史以来从没有如此众多的人大大收益于如此简单的代码!”而且多数语言和平台的xUnit架构都是大同小异,有的仅是语言不 同,其中最有代表性的是JUnit和NUnit,后者是前者的创新和扩展。一个单元测试框架xUnit应该:1)使每个TestCase独立运行;2)使 每个TestCase可以独立检测和报告错误;3)易于在每次运行之前选择TestCase。下面是我枚举出的xUnit框架的概念,这些概念构成了当前 业界单元测试理论和工具的核心:

[测试方法/TestMethod]
测试的最小单位,直接表示为代码。

[测试用例/TestCase]
由多个测试方法组成,是一个完整的对象,是很多TestRunner执行的最小单位。

[测试容器/TestSuite]
由多个测试用例构成,意在把相同含义的测试用例手动安排在一起,TestSuite可以呈树状结构因而便于管理。在实现时,TestSuite形式上往往也是一个TestCase或TestFixture。

[断言/Assertion]
断言一般有三类,分别是比较断言(如assertEquals),条件断言(如isTrue),和断言工具(如fail)。

[测试设备/TestFixture]
为每个测试用例安排一个SetUp方法和一个TearDown方法,前者用于在执行该测试用例或该用例中的每个测试方法前调用以初始化某些内容,后者在执行该测试用例或该用例中的每个方法之后调用,通常用来消除测试对系统所做的修改。

[期望异常/Expected Exception]
期望该测试方法抛出某种指定的异常,作为一个“断言”内容,同时也防止因为合情合理的异常而意外的终止了测试过程。

[种类/Category]
为测试用例分类,实际使用时一般有TestSuite就不再使用Category,有Category就不再使用TestSuite。

[忽略/Ignored]
设定该测试用例或测试方法被忽略,也就是不执行的意思。有些被抛弃的TestCase不愿删除,可以定为Ignored。

[测试执行器/TestRunner]
执行测试的工具,表示以何种方式执行测试,别误会,这可不是在代码中规定的,完全是与测试内容无关的行为。比如文本方式,AWT方式,swing方式,或者Eclipse的一个视图等等。

{实例:Fibonacci数列}

下面的Sample展示TDDer是如何编写一个旨在产生Fibonacci数列的方法。
(1)首先写一个TC,断言fib(1) = 1;fib(2) = 1;这表示该数列的第一个元素和第二个元素都是1。

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
}

(2)上面这段代码不能编译通过,Great!——是的,我是说Great!当然,如果你正在用的是Eclipse那你不需要编译,Eclipse 会告诉你不存在fib方法,单击mark会问你要不要新建一个fib方法,Oh,当然!为了让上面那个TC能通过,我们这样写:

public   int  fib(  int  n ) {
        
return   1 ;
}

(3)现在那个TC亮了绿灯,wow!应该庆祝一下了。接下来要增加TC的难度了,测第三个元素。

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
        assertEquals(
2 , fib( 3 ));
}

不过这样写还不太好看,不如这样写:

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
        assertEquals(fib(
1 ) + fib( 2 ), fib( 3 ));
}

(4)新增加的断言导致了红灯,为了扭转这一局势我们这样修改fib方法,其中部分代码是从上面的代码中Ctrl-C/Ctrl-V来的:

public   int  fib(  int  n ) {
        
if  ( n  ==   3  )  return  fib( 1 ) + fib( 2 );
        
return   1 ;
}

(5)天哪,这真是个贱人写的代码!是啊,不是吗?因为TC就是产品的蓝本,产品只要恰好满足TC就ok。所以事情发展到这个地步不是fib方法的错,而是TC的错,于是TC还要进一步要求:

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
        assertEquals(fib(
1 ) + fib( 2 ), fib( 3 ));
        assertEquals(fib(
2 ) + fib( 3 ), fib( 4 ));
}

(6)上有政策下有对策。

public   int  fib(  int  n ) {
        
if  ( n  ==   3  )  return  fib( 1 ) + fib( 2 );
        
if  ( n  ==   4  )  return  fib( 2 ) + fib( 3 );
        
return   1 ;
}

(7)好了,不玩了。现在已经不是贱不贱的问题了,现在的问题是代码出现了冗余,所以我们要做的是——重构:

public   int  fib(  int  n ) {
        
if  ( n  ==   1   ||  n  ==   2  )  return   1 ;
        
else   return  fib( n  -   1  )  +  fib( n  -   2  );
}

(8)好,现在你已经fib方法已经写完了吗?错了,一个危险的错误,你忘了错误的输入了。我们令0表示Fibonacci中没有这一项。

public   void  testFab() {
        assertEquals(
1 , fib( 1 ));
        assertEquals(
1 , fib( 2 ));
        assertEquals(fib(
1 ) + fib( 2 ), fib( 3 ));
        assertEquals(fib(
2 ) + fib( 3 ), fib( 4 ));
        assertEquals(
0 , fib( 0 ));
        assertEquals(
0 , fib( - 1 ));
}

then change the method fib to make the bar grean:

public   int  fib(  int  n ) {
        
if  ( n  <=   0  )  return   0 ;
        
if  ( n  ==   1   ||  n  ==   2  )  return   1 ;
        
else   return  fib( n  -   1  )  +  fib( n  -   2  );
}

(9)下班前最后一件事情,把TC也重构一下:

public   void  testFab() {
        
int  cases[][]  =  {
                {
0 0 }, { - 1 0 },   // the wrong parameters
                { 1 1 }, { 2 1 }};   // the first 2 elements

        
for  ( int  i  =   0 ; i  <  cases.length; i ++ )
                assertEquals( cases[i][
1 ], fib(cases[i][ 0 ]) );

        
// the rest elements
         for  ( int  i  =   3 ; i  <   20 ; i ++ )
                assertEquals(fib(i
- 1 ) + fib(i - 2 ), fib(i));
}

(10)打完收工。

{关于本文的写作}

在本文的写作过程中,作者也用到了TDD的思维,事实上作者先构思要写一篇什么样的文章,然后写出这篇文章应该满足的几个要求,包括功能的要求(要 写些什么)和性能的要求(可读性如何)和质量的要求(文字的要求),这些要求起初是一个也达不到的(因为正文还一个字没有),在这种情况下作者的文章无法 编译通过,为了达到这些要求,作者不停的写啊写啊,终于在花尽了两个月的心血之后完成了当初既定的所有要求(make the bar green),随后作者整理了一下文章的结构(重构),在满意的提交给了Blog系统之后,作者穿上了一件绿色的汗衫,趴在地上,学了两声青蛙 叫。。。。。。。^_^

{后记:Martin Fowler在中国}

从本文正式完成到发表的几个小时里,我偶然读到了Martin Fowler先生北京访谈录,其间提到了很多对测试驱动开发的看法,摘抄在此:

Martin Fowler:当然(值得花一半的时间来写单元测试)!因为单元测试能够使你更快的完成工作。无数次的实践已经证明这一点。你的时间越是紧张,就越要写单元测试,它看上去慢,但实际上能够帮助你更快、更舒服地达到目的。
Martin Fowler:什么叫重要?什么叫不重要?这是需要逐渐认识的,不是想当然的。我为绝大多数的模块写单元测试,是有点烦人,但是当你意识到这工作的价值时,你会欣然的。
Martin Fowler:对全世界的程序员我都是那么几条建议:……第二,学习测试驱动开发,这种新的方法会改变你对于软件开发的看法。……

——《程序员》,2005年7月刊

{鸣谢}

fhawk
Dennis Chen
般若菩提
Kent Beck
Martin Fowler
c2.com

(转载本文需注明出处:Brian Sun @ 爬树的泡泡[http://www.blogjava.net/briansun])

 

2011-01-09 11:44:00 weixin_30275415 阅读数 8
  • Android底层技术:Linux驱动框架与开发

    于此,将框架(Framework)和设计模式(Design Pattern)应用于Linux驱动开发,说明了如何以面向对象、设计模式和框架概念来看待Linux驱动程序的架构。其直接的益处就是:让我们能基于一致的设计理念来结合Android HAL与Linux两层的驱动开发,规划出整体和谐Android-based软硬整合产品架构。

    22346 人正在学习 去看看 高煥堂

【IT168 案例】 自从编程界的领袖们发表旨在通过接受需求变更,加强同用户合作,缩短软件提交周期来改善软件开发过程的敏捷软件开发宣言至今已近10年之久了。

  敏捷宣言制定2001年2月,当时一群软件开发者聚集在犹他州,他们希望能找到一种可以替代那些由文档驱动的、“重型”的软件开发模式(如当时的被当作金牌标准的瀑布模型方法)的新方法。

  尽管早在犹他州会议之前,敏捷开发方法就已经出现,但这次会议却被当作这种方法论推广进程中的一次分水岭事件。十年以来,敏捷开发已被众所周知,很多软件 公司采纳了Scrum和XP(极限编程)等敏捷开发实施方案。尽管还存在着不可预知的问题,敏捷方法领域里的专家都认为,总的来说,敏捷方法的实施会给软 件开发活动带来益处。

  “我说过,我们改变了这个行业,”一位宣言的签署者、目前在Tektronix工作的Ward Cunningham这样说。由于敏捷的出现,关于计算机编程的没落和编程危机的讨论逐渐消失,他说:“我们已经再也听不到人们谈论这个话题了。”

敏捷开发十年 成效几何?

  敏捷宣言比实际预期要成功的多,IBM Rational部门的首席敏捷和Lean方法论导师Scott Ambler这样说。

  “它对我们整个行业有着重大的影响,”Ambler说。“如今你已经很难找到有不想去试试敏捷方法的人了。跟传统的开发方法相比,人们希望使用敏捷开发和迭代开发来使项目获得成功的愿望要强烈的多。“

  但是Kent Beck,同样也是一位宣言的签署者,并且是XP的创始人,在宣言签署的10年后,对敏捷开发所带来的好处去并不是那么认可:“对于这个问题我没有一个几句话的答案。”

  “敏捷开发是让人们更加认真仔细的思考如何开发软件,”Beck说。然而,并不是每个人都在敏捷开发上走对了路,他提示说。“仍然有很多人喜欢把读来的一些建议指导应用到他们的项目上,其实那些根本不是所谓的敏捷开发,“Beck说。

  敏捷开发的条件

  敏捷开发很难学,Cunningham说;”在你能够使用这套方法论前你必须掌握精通各种技巧。“

  敏捷开发需要你扎实的技术功底,Cunningham强调道。”有很多人闯进这个领域后发现编程枯燥乏味,不再想学。“Cunningham说:”你要有兴趣做它,想把它做好,这样才有助于你成功。“

  “来自企业组织的阻碍会在敏捷方法论的实施过程中显现出来。敏捷开发鼓励更加频繁的交付软件,鼓励把事情分解成小块,而不是把整个项目看成一块。”Skip Angel — 工作于BigVisible Solutions的一位敏捷顾问这样说。”我想这些对于一些企业是个挑战,这些企业的运营方式并不能使他们可以做敏捷的交付。“

  项目在一些耗时的过程中很可能会陷入泥潭,Angel补充道,开发人员应该使用持续集成来避免这种瓶颈。

  敏捷开发不是银弹,Ian McLeod–做应用软件生命周期管理工具的SmartBear Software公司的执行副总裁这样说。”你需要把事情做对 … 你的敏捷开发可能做的很失败,“ 他说。

  Beck回忆起1997年用敏捷开发方法成功的开发出JUnit Java单元测试工具。他们团队使用短周期迭代,大量的单元测试,紧密和客户进行沟通。

  “它使我们开发的更快,使我们更好的清楚需要去做的事情,”Wade Weston — 开发标准化交流系统的AttainResponse公司的CEO 这样说。“AttainResponse每周进行开发工作的sprints。我们的sprints周期很短,我们把精力高度的集中于本周要做的工作。 ”Weston说。

  “‘可是让每个人都能上手仍然是个问题,’我的一个兄弟经常对我这样说,他喜欢更详细明确的需求。我一直告诉他,我们之所以开发的这么快,就是因为我们没有明确的需求,”Weston说。等待核心的需求说明基本上是浪费时间。他补充道。

  “有些时候,一些开发人员说他们在做敏捷开发,可事实他们根本不是,”Damon Poole — 提供敏捷开发项目管理软件的AccuRev公司的CTO 这样说。“有些开发人员2周都不能把开发的东西(或“故事”)完整的编译集成,”他说。“如果你真的是做敏捷开发,那2周的时间足够把用户故事发布 了。”Poole说。

  敏捷编程的多种实施方案

  Scrum 和 XP 是两个最具有代表性的敏捷方法论。Beck把XP描述为更注重开发的技术方面的方法。“XP说的更多的是告诉程序员应该做什么,相对比,Scrum是一种项目管理方法论”他说。

  “XP的与众不同之处在于它是一种体系,而不是一种解决方案。”Cunningham — 一位推动XP发展的贡献者这样说。“它是一种有计划的编程方式。”

  Scrum专注于如何管理和交付你的产品,而XP却是考究于如何去做你的工作,Angel说。

  Poole指出,“很明显Scrum和XP是目前两种主要的方法论,你经常能看到Scrum团队会采纳XP技巧,而XP团队也会使用Scrum概念。”

  另外一种敏捷方法论是Kanban,它起源于制造业生产流程和Lean软件开发概念,Poole说。Kanban里的约束很少,它关注于如何使价值反馈给客户的过程,他解释说。Lean关注于组织效能优化,价值优化,降低浪费,确保正确的好的生产过程,Angel补充说。

  RUP(Rational Unified Process)也被人们称作为一种敏捷方法,尽管这种说法有待商榷,McLeod说。RUP的特点是有一大堆的文档,它可能是针对敏捷方法中的各个步骤 的,他解释说。RUP可以是一种敏捷方法,Ambler说:”RUP给予我们的是流程上的架构准则。它完全依赖于你是如何制定的。“

  Ambler同时提到了DSDM — Dynamic Systems Development Method — 一个敏捷领域里的失败的案例。SDSM有点像RAD [rapid application development],但在里面增加了一下额外的处理。RAD跟敏捷开发的不同之处在于它只关注开发迭代,而不考虑促进合作,他指出。

  McLeod认为各种敏捷方法论和迭代开发过程很相似。”它们之间没有太多的区别,“他说。

  “敏捷”这个术语,Cunnigham说,是在犹他州会议上选出的一个词,人们通常把它引用为”轻量级“的方法,他回忆到。但”轻量级“这个词从表面意思上看也承载着一些负面的含义,他说。

转载于:https://www.cnblogs.com/mengheyun/archive/2011/01/09/1962803.html

国内所需

阅读数 235

没有更多推荐了,返回首页