测试驱动开发的案例

您是否读过别人的代码并想过: “那真是太美了,希望我能像这样编码” ? 答案很简单:没有人第一次尝试使用这种代码。 好的代码始终是几次重构,迭代,检查和改进的结果。 大部分时间都是由一个以上的人完成的。
因此,作为开发人员的目标应该是编写我们能做的最好的代码。 为了做到这一点,我们应该考虑拥有尽可能多的工具和技术。 我总是推荐极限编程和测试驱动开发。
关于TDD
让我们稍微提醒一下TDD算法的基础知识:
- 我的GTD重新启动第3部分:完成应用程序
- 清洁卧室的6个简单步骤
- Google在5分钟内成为专业人士
- “懒惰的星期天下午” –拉比·加姆利尔·雷斯佩斯–中
- 如何成为成功的拖延者– Daniel Mcmorrow –中

以下顺序基于肯特·贝克(Kent Beck)的《示例进行测试驱动的开发》一书,并摘自Wikipedia:
- 添加测试:编写定义功能的测试,该功能应该非常简洁。 要编写测试,开发人员必须清楚地了解功能的规格和要求。 开发人员可以通过用例和用户案例来满足需求和异常条件,并可以在适合软件环境的任何测试框架中编写测试。 这使开发人员在编写代码之前将重点放在需求上。
- 运行所有测试,看看新测试是否失败:这验证测试工具是否正常工作,表明新测试不会通过而不需要新代码,因为所需的行为已经存在,并且排除了新测试的可能性有缺陷,将永远过去。 由于预期的原因,新测试将失败。 此步骤可提高开发人员对新测试的信心。
- 编写代码:下一步是编写一些使测试通过的代码。 在此阶段编写的新代码并不完美,例如,可能会以微不足道的方式通过测试。 这是可以接受的,因为它将在步骤5中进行改进和磨练。此时,编写代码的唯一目的是通过测试。 程序员不得编写超出测试所检查功能的代码。
- 运行测试:如果现在所有测试用例都通过了,则程序员可以确信新代码符合测试要求,并且不会破坏或降低任何现有功能。 如果没有,则必须对其进行调整,直到新的代码为止。
- 重构
由于我一直在使用TDD进行编码,因此每次我试图说服他人也这样做时,我会一遍又一遍地面对相同的原因。 我称之为借口的原因:
借口1:我不相信
很好,您不必。 TDD不是信仰问题,而是工具。 我不需要相信螺丝刀,它们只是。 当然,我可以用锤子解决所有问题。 也许如果我受够了重击,我可以用锤子将螺丝钉固定在墙上。 但是仅仅因为我熟悉我的锤子并且知道如何使用它,并不意味着它是在每种情况下的正确工具。

“如果您仅有的工具是锤子,我想把所有东西都当作钉子来对待是很诱人的。” (亚伯拉罕·马斯洛,1966年,仪器定律)
扩展工具箱并使用适合每个问题的方法是我们的责任。 说“我不相信”,并以此为借口交付不良(或不够好的代码)代码是不负责任的。
不要相信它,只是测试一下,试一下,得出自己的结论。
那不是你不喜欢TDD的原因
借口2:它不适用于我的项目
许多知名公司每天都在使用TDD,但是您的项目是如此独特,您的要求如此特殊,以至于您无法使用该技术……
大多数情况下,您没有设置适当的测试环境,并且当您处理具有某些外部依赖性的类(例如数据库,队列系统或类似的东西)时,您将找不到正确的测试方法。 那本身就是代码的味道,应该让您重新思考代码: 好的代码易于测试。
如果您必须注入许多依赖关系,请重新创建一个复杂的“上下文”或嘲笑一半来编写测试……也许您的代码设计不是最好的。
当您使用TDD从头开始一个项目时,这种情况不会发生,但是在大多数情况下,您将不得不适应现有的代码。 在那种情况下,进行方法是配置使TDD变得舒适的必要工具。 通常包括:
- 一些BDD或断言框架
- 一些代码覆盖率框架
- 一些模拟装置
我尝试使用所有这些工具设置来编译种子项目,因此下一次我不得不处理没有这些工具的现有代码时,可以轻松地添加它们。 看看我自己做的一些种子:
- Java Spring云种子,用于使用TDD进行编码(使用Spock代替JUnit)
- 使用TDD进行编码的Javascript种子
那不是你不喜欢TDD的原因
借口#3:花费太多时间,会使团队进展缓慢
您可能会觉得自己的速度变慢了,刚开始时毫无疑问。 您必须设置所有引擎和机制,才能保证对所有内容进行测试,这需要时间。 只有花费时间并致力于这项技术,您才能看到从长远来看,您将比其他时候快得多。
当使用TDD进行编码时,代码库可以更好地进行修改和改进,这使您可以更快地添加功能:

当然,为了说服产品所有者和利益相关者,您总是可以争辩说时间会浪费金钱:

考虑到这些指标,将TDD用作开发技术确实是负责任的,因为它将使您能够扩展和维护项目。 因此,抵制代码“快速”的诱惑,然后做正确的事。
那不是你不喜欢TDD的原因
您不喜欢TDD的真正原因
你不喜欢TDD是因为很难 。
别难过:TDD很难
一开始,我也说了所有这些话。 然后我开始做卡塔舞和练习,然后我想:“嗯,这对练习体操或代码体操很有用”,但从来没有胆量在实际工作中使用它。 然后,我开始阅读意见并关注有趣的人,例如杰森·戈曼(Jason Gorman)。 看一下他对katas和“真实世界代码”的看法:

然后,我与同事Finner在工作中交谈,告诉了他当时的情况,他提议在与TDD合作开发下一个功能时与他坐在一起。 突然之间,一点点编程对一切就变得清晰了。 后来,我决定始终使用TDD进行编码,直到找到新的更好的编码方法为止。 我一直乐于改进。
TDD是简单而不是简单的策略之一。 该算法易于理解(红色,绿色,重构),但是很难正确实施。 最初的尝试总是令人沮丧,因为这是一种反直观的做法。
它迫使您在代码甚至还没有存在之前就考虑一下代码的设计,这使您无法执行编码人员喜欢做的事情:代码。 当使用TDD进行编码时,您不能跳过键盘并开始输入,您不得不考虑如何使最终代码看起来像,即使如此,也要迭代几次直到达到最终实现。 即使这样,也可以肯定,该实现会随着时间而改变。
记住宫城老师: “专心。 只想一棵树。 制作完美的照片,直到最后一片叶子。 擦干净心灵,除了树。 全世界都不存在。 只是树。 像图片一样制作。”

完美描绘您希望代码看起来像什么,然后不断前进,直到到达那张图片。
当IDE上的东西变红时不要惊慌
当代码未编译或完全不完整时,它会产生很大的压力和焦虑。 IDE创建警报并将代码标记为错误,您会感到需要立即对其进行修复。 只是抵抗一下浪潮,让IDE抱怨并继续进行,让代码暂时不能编译。 以下是TDD测试的示例:
Circle circle = ShapeBuilder.CIRCLE;
Assert.assertNotNull(circle.area());
很自我解释。 问题是Circle类和ShapeBuilder类还不存在,IDE会抱怨。 在编写实际测试之前,很难拒绝实现它们: Assert.assertNotNull(circle.area());
IDE会用circle.area()尖叫,因为甚至没有该类,因此area方法应该如何工作? 深吸一口气。 使用自动完成功能,或生成代码(无论您的IDE如何调用它)并创建类圈子。 使它实现接口Shape(不存在),依此类推……
当事情变红时不要惊慌,使用IDE的建议,您会发现在大多数情况下,它建议创建失败的事物。
参考文献:
- Stocksnap 在 Stocksnap 上 拍摄的 照片
- 仪器法则
- 空手道小子剧本
- 测试驱动开发。 通过示例(Addison-Wesley签名系列)
- 测试驱动的开发可能看起来是工作的两倍-但无论如何您都应该这样做
- 测试驱动的开发Wikipedia页面