在InfoQ发表的一篇文章《实施TDD时的常见问题》中, Chad Meyers提出了关于TDD实施的问题,如下所示:

  1. 我该容忍多大限度的预先设计?你怎么知道应该何时停止(也就是说,“当人们开始讨论算法,就是该测试的时机了”)?
  2. 对于象“我心里清楚我们需要这个”这类东西——我们该如何处理(例如,在控制台main()方法中加上一个try/catch{Console.WriteLine(ex);}?)
  3. 编写测试时,为了让代码编译通过,你不得不写下一两个接口,一个实体类,在类中还有一些NotImplementedExceptions等等。这一步该走多远?
  4. 如果在测试一个用户故事期间,你发现先前的预先设计有问题,你是会马上停下来跟你的搭档讨论,做该做的事,然后继续;还是折返回去,在当前的故事中采用完整的测试优先模式?
  5. 在处理一个新的用户故事时,你发现针对前一个故事所编写的测试已经不再体现需求。你是否会立刻重构那个测试,还是把它标记为“忽略”,等你完成当前的故事再回过头去处理那个被忽略的测试?还是有其它做法?
  6. 如果新的用户故事要求对某个已有的测试做出轻微调整,你是会调整它,还是会写一个新测试,把旧的扔掉(也就是,“不许更改现有的测试代码!”,或者“只有出现小的编译问题时才准动它,否则就别碰!”)?
  7. 如果你构建了模型并且通过了测试,但是你发现这个设计很幼稚,而且即将要做的用户故事肯定会对其进行重大修改,并生成一个新的完全不同的模型。你是应该退 一步,考虑进行大型重构吗?还是应该继续修修补补,调整现有的模型,尽管这个模型最终会被目前看来显然超出该用户故事范围的工作所改进?

Derick Bailey分享了他对Chad这些问题的看法。以下是他的回答的摘录:

  1. 知道自己应该构建什么就行。
    这个问题实际上应该根据你的实际项目来回答。我最近做了一个企业集成项目,里面用到了我们的核心维护系统,然后添加了一些离线操作功能。在这个项目中,我 没有做太多的预先设计。我知道系统需要能够在离线模式下操作,我还知道即使是没人登录计算机或者运行这个软件时,它也需要能够执行一些操作,我知道它需要 跟主维护系统进行双向通信,用来执行与维护相关的的一定数量的任务。我从一些设想/预先设计开始动手——构建了一个Windows Service来运行核心进程,构建了一个客户端-服务器架构来托管UI,在一个消息系统上为通信提供支持。除了这些算是预先设计以外,在构建软件的过程 中都是其他各项任务的功能需求来驱动项目设计的进展。
    最近,我与一个为实验室开发某系统的团队一起工作时,有了截然相反的体验。我们有源于客户的需求,客户所要求的流程,以及对客户手中数据进行转换的需要; 我们需要一个大规模的预先设计,用以判断我们的解决方案可以满足数据转换和功能需求。系统的核心设计是预先完成的,但是具体实现还是在开发过程中慢慢成 型。
  2. 这 是一个让我在心里做斗争的问题,回答它需要视出现这个问题的时机。我并不希望添加特性和功能,除非我知道现在需要它们——当这个问题涉及到应用程序的域模 型和核心基础架构时。然而,对于类似于Console.WriteLine(ex)之类的问题,要将这些功能添加到应用程序的Main方法中,我认为没有 什么好争论的。毕竟——你并不会对应用程序的Main方法做单元测试……你会对它进行集成测试。
  3. 对于实现接口,我并不希望手动地编写桩 对象。相反,我会试着尽可能使用RhinoMocks,这样就能避免这一问题。我过去习惯于在任何时候都要编写桩代码,其中包括十几个未实现的方法——结 果导致我的单元测试难以维护,同时还为确定的单元测试中确定的方法提供了大量的重复桩对象。

    你应该只添加你需要的方法。如果你拥有一个真实的对象,它包含了将要抛出的未实现的异常,你可能要检查一下这个方法,看它是否能够从代码库中删除。

  4. 这一问题仍然要视具体的问题而定。

    我花了很多时间和我的合作者开展结对编程。我们不断遇到这样一个场景,即下一个功能需求会导致现有设计的改变。如果发生了这样的事情,我们只会对我们需要的 部分进行修改。我们会为某些新功能编写新的单元测试,如果之前的设计使得我们的单元测试无法通过,我们就会返回修改之前的设计——对在修改范围内的单元测 试进行修改与更新。我们要将在这种情况之下的单元测试看作是“必做列表”中的内容。当我们修改已经完成的设计时,我们通常会忘记这个已经修改的范围,或者 忘记我们试图满足的新的功能需求。所幸,通过对整个单元测试套件的完整运行,可以显示被破坏的测试,提醒我们需要修改。

    在结对编程期间,我们有很多机会获得新的需求,回顾我们的设计与实现,通过对这一特定需求的理解,认识到整个模型是错误的,我们需要从头编写。此时,我们做了 许多人想都没有想过的事情——我们删除了系统中的所有类和单元测试,然后从头再来。通过这种方法获得的经验教训以及对其他需求的理解,可以引导我们开始一 个新的设计,它能够考虑我们需要的功能——但是,我们仍然能够在我们需要的基础上,编写现有的功能。我们虽然没有创建“未来的代码”,但通过额外的接口和 抽象提供了可扩展性……

    我已经反复强调了这样一个循环:“设计、修改、重新设计、修改、重新开始构建、修改”。根据我的经验,最终的结果值得我们这样去做——系统中更少的bug,更加准确地表示业务需求的模型,更好的扩展性和灵活性,从而允许在未来作出修改。

  5. 尽早地修复它。如果你现在不修复它,你可能会在以后做更多的工作,因为会有一些新的内容与这些错误的内容产生交互,从而使得你必须同时修改错误的内容与新的内容。如果你现在就修改错误的内容,就不必在后面重写新的内容了。
  6. 如 果你正在修改一个测试所指的含义(例如,对测试进行重命名,使得它准确表达测试的内容与原因),那么,你可以自动地删除一个旧的测试,创建一个新的——即 使你只是从语义上对它进行删除,你仅仅只是进行重命名以及修改很少的细节。如果测试代表的含义仍然是相同的,你就可以只修改某些实现细节(因为根据根据问 题4,你修改了设计),然后保留这个测试,而只是修改实现去满足新实现的需求。
  7. 参见问题4的回答。

此外, Derick Bailey还谈到了其他有关TDD的问题,例如对断言的比较,对SetUp方法的分析,对重构工具的认识等。

对于这7个问题,我的回答如下:

1、 预先设计的度并没有一个准确的值。首先对于一个系统而言,我们应该从领域与架构的角度分析技术关键点,并对这些关键点进行预先设计,是非常有必要的。此 外,对于系统的infrastructure的内容,我们也需要预先设计,除非这些内容是已经存在的。其实,如果采用领域驱动设计的方式开展对领域逻辑的 分析,那么何时开始做测试,应该没有太大的歧义,它自有一套规则需要我们遵循。TDD并不是完全排除设计的作用,利用测试来驱动开发,其实同时也代表我们 可以利用测试来驱动设计。因此,我的意见是如果将系统各个组成部分看作是package或者component,那么理想地TDD起点应该是在 package或component被划分好之后,我们就可以利用单元测试来驱动我们对领域模型的思考与编码实现了。

2、这个问题没有什么好说。如果是问题中的例子,我想没有什么好争论的,正如Derick Bailey所说,我们确实不会对Main方法进行单元测试。我赞成Derick的意见。

3、 显然,如果能够利用工具来模拟桩对象,何乐而不为?如果测试代码依赖的接口和实体类,本身就是我们在实现系统时就需要的,那么实现它们算不上是无用功,此 时,我赞成先去定义或者实现它们。不过在定义和实现它们的同时,需要考虑到这些定义不一定完整与准确,我们还要考虑随时会对其进行修改,修改后,还要必须 检验你的测试代码。

4、既然发现有问题,为什么要暂时放过,以观后效呢?我的思想一贯都是要将错误扼杀在摇篮中。否则,因为一时偷懒放过的这个问题,往往会在后面给我们带来无穷无尽的烦恼。如果你不能确认这些错误,拿出来讨论是一个比较好的办法。

5、与问题4相似。

6、 具体看修改的范围。如果测试需要修改的不会影响依赖它的测试用例,即不会修改它的外部接口,那么只需要修改部分实现代码就可以了。没有必要去专门编写一个 新的测试用例。如果涉及到对外部接口的需要,可以考虑重构。例如Derick提到的重命名的问题。如果重构不能解决,那就可以编写一个新的测试。

7、很显然,与问题4相同。

转载于:https://blog.51cto.com/wayfarer/280173

实施TDD时的常见问题相关推荐

  1. 求职应聘时面试常见问题1

    求职应聘时面试常见问题巧回答 1.请你自我介绍一下你自己? 回答提示:一般人回答这个问题过于平常,只说姓名.年龄.爱好.工作经验,这些在简历上都有.其实,企业最希望知道的是求职者能否胜任工作,包括:最 ...

  2. CAD机械制图时的常见问题

    在CAD机械制图时,尽管我们对CAD机械绘图的常识性问题很了解,但是在我们绘图的过程中,不免会遇到一些其他的问题,当我们遇到这些问题的时候,应该如何解决,才会不影响我们的绘图操作? CAD机械制图时的 ...

  3. LabVIEW使用NI Vision模式匹配时的常见问题

    LabVIEW使用NI Vision模式匹配时的常见问题 该如何使用模式匹配功能?为什么在进行模式匹配时会出现"无效模板"错误? 解决方案 模式匹配可以在灰度图像中快速地定位与某一 ...

  4. 苹果系统更新服务器繁忙,如何解决升级MacOS 10.13时的常见问题

    随着Mac OS X 10.13 High Sierra的测试版发布,很多Apple粉丝和发烧友都更新了他们的系统.由于此系统是测试版,因此存在一些bug.有些用户在升级系统后会遇到与Mac OS X ...

  5. android 开发 佳博打印模板_电商在打印快递电子面单时的常见问题及解决方法

    电商经营流程中有发货这一环节,而发货时需要在外包装上面贴一张标签.这张标签就是用电子面单打印机打印出来的,那么打印机工作过程中,出现设置问题或小故障怎么办?佳博打印机小编在这里总结了一些电子面单打印机 ...

  6. ai项目实施_公司在实施AI时必须避免的6个最大陷阱

    ai项目实施 The age of AI is upon us and many companies begin to start their AI journey and reap the full ...

  7. 注意:出海企业选择CRM系统和实施团队时需要避开这些坑

    近几年,国内掀起了一股前所未有的国潮品牌浪潮.中国品牌的逐步重塑也缩小了与国外品牌的质量差距,提升了国际形象.在赢得海外市场之前,作为国潮基础的国产品牌率先赢得了当地消费者的尊重.民族潮流的背后,是民 ...

  8. 安装 WordPress 时一些常见问题

    1)安装 WordPress 时,输入数据库信息后提交之后,却直接弹出一个空白页面 解决方法很简单,打开PHP的配置文件php.ini,查找max_execution_time,将这个参数改为max_ ...

  9. Intel Realsense D435 关于深度摄像头获取实际深度坐标时的常见问题及可能的解决方案

    20191017 获取深度有时或遇到黑洞,或者取值不准直接"贯穿",导致深度直接扩大.缩小(小概率)或直接为0(当出现黑洞或深度超出可监测范围时就为0),今后可使用如下算法:检测目 ...

最新文章

  1. 揭开人类语言的神秘面纱:从理解到处理自然语言
  2. 何时会调用拷贝构造函数
  3. DOMException: Failed to execute ‘appendChild‘ on ‘Node‘: This node type does
  4. 计算机硬件基础大纲,计算机硬件基础教学大纲..docx
  5. async await 的前世今生(Updated)
  6. Git 添加和提交组合命令
  7. 石子合并(洛谷-P1880)
  8. python从入门到精通需要多久-Python 从入门到精通:一个月就够了!
  9. Windows下安装最新的Apache+PHP+MySQL方法--记录方便自己参考
  10. 人工智能成功与冠状病毒抗争,但个人隐私令人担忧
  11. excel保存快捷键_Excel快升效率的快捷键
  12. 火狐浏览器打开书签 不要在新页面打开书签
  13. CCF 202206-2 寻宝!大冒险!
  14. CrackMe160 学习笔记 之 024
  15. Java程序设计 试卷A
  16. 「C#」异步编程玩法笔记-WinForm中的常见问题
  17. 暴雪修改手机500服务器错误,改造:500内部服务器错误
  18. asp.net core ABP模板本地化设置
  19. 猴子吃桃,C语言,递归法
  20. IT管理体系——战略、管理和服务

热门文章

  1. Windows核心编程 第十一章 线程池的使用
  2. Intel汇编语言程序设计学习-第三章 汇编语言基础-下
  3. hdu 4891 模拟
  4. C语言经典例96-计算字符串中子串出现的次数
  5. 【五线谱】符干朝向与连音线 ( 符干朝向 | 第三线以下符干朝上 | 第三线以上符干朝下 | 连音线 )
  6. 【计算理论】计算理论总结 ( 上下文无关文法 CFG 转为下推自动机 PDA 示例 1 ) ★★
  7. 【Java 并发编程】CountDownLatch 简介
  8. WPF解决WindowsFormsHost背景透明
  9. 判断两个数组内容是否相同
  10. ASP.NET Core 中文文档 第四章 MVC(2.2)模型验证