测试驱动开发(TDD)是一种软件工程实践,要求在应该验证的代码之前编写单元测试。 TDD源自敏捷世界,它是极限编程(XP)方法的基本实践,如今,TDD本身已被公认为是一门学科,并且还在敏捷环境之外使用。 概述和实践介绍,您将无法没有!
在过去的15年中,敏捷运动不断发展,从而导致了新的,更务实的实践,旨在为客户提供能够尽快满足其需求的计划。 对于此问题, 基于V模型的传统方法提供了“最后测试”方法,并在应用程序代码之后编写了单元测试。 这种方法已经清楚地显示了其随时间的局限性,因为充其量,书面测试适合于代码,而不是相反,从而引入了确认偏差。 在最坏的情况下,由于代码似乎正常工作,甚至没有编写这些测试,为什么还要浪费时间编写它们?
对于TDD支持者来说,这个问题的答案是显而易见的,他们清楚地知道,今天在测试上进行的投资将来会在添加新功能或重构操作时在很大程度上退还。 因此, TDD采用“测试优先”的方法,即在代码之前编写单元测试 ,唯一的原因将是成功执行这些测试。 这个想法可以追溯到远古时代,由肯特·贝克 ( Kent Beck )在1990年代中期正式提出,他将其作为极限编程(XP)方法论的支柱之一 。 然后有必要等到2003年,并出版《 测试驱动的开发:通过示例 》这本书,才能看到它完成了实践的编纂。 后者将“持续重构”的概念添加到“测试优先”方法中,以改善产品代码。
通过将编程,单元测试编写和重构相结合 , TDD是一种结构化实践,它允许获取干净的代码 ,易于修改和满足所表达的需求,而这在开发应用程序时仍然是首要任务。 TDD分为三个阶段 :
这三个阶段的实现在构成TDD周期的5个主要阶段内完成(图1)。
图1:TDD周期此周期很短(最多10分钟),并重复进行直到成功完成涉及功能的所有单元测试为止 。 这些步骤似乎很简单,但是必须极其严格地应用它们才能充分利用TDD。 对规则的这种严格监控使获得符合一定数量的良好开发实践的质量代码成为可能。 因此,在考虑代码重复( DRY原则— 不需要 )时,所获得的代码对基本的( KISS原则-保持简单,愚蠢 )不寻求实现不必要的服务( YAGNI原则-不需要)很满意。 重复自己 ),这要归功于它的不断重构。 这样,程序的实现遵循增量逻辑,从而允许出现灵活的模块化体系结构。
TDD并不是一个毫不费力地拥有最佳单元测试套件的奇迹解决方案 。 重要的是要记住,在这种实践中, 测试代码比生产代码同样重要! 因此,随着时间的流逝,维护测试代码至关重要。 为了有效, 测试必须干净整洁,其特点是可读性强 。 这是通过执行尽可能简单,清晰和尽可能密集的测试而获得的,即使他用最少的代码说出尽可能多的内容。 因此, 单元测试应该仅代表一个概念,并且仅包含一个断言 。
最后, 干净的测试还必须遵循其他5条规则 ,可以使用首字母缩写FIRST轻松记住这些规则 :
TDD方法是开发人员的范式转变。 学习阶段是必要的,在此阶段中,随着开发人员技能的增加,收益将越来越多。 因此,将TDD视为未来的投资。 更改还涉及文档存储库,其中包含代表可执行规范和应用程序文档的单元测试。 实际上,如果使用测试来验证需求,它们也可以描述需求。 基于此观察,主要困难将在于开发人员以计划的测试形式开发复杂功能路线图的能力,并能够在必要时提出质疑。
TDD允许尽早发现问题 ,从而降低了解决成本,并减少了错误数量。 单元测试系列提供的代码覆盖范围旨在最大,最小覆盖80%以上。 该安全网使开发人员有信心进行重构和持续改进工作 ,这对于该方法的成功至关重要。 实际上, 技术债务可以通过灵活,可维护和可重用的代码来更好地控制 ,这些代码有助于添加新功能。
最后, TDD的ROI也反映在开发人员生产率的整体提高中 。 确实,如果它推动一般的写入速度快两倍,那么它将在重构阶段导致真正的浪费搜寻,从而导致一半的代码。 掌握了学习内容后,我们观察到开发人员的工作方式发生了范式转变,从调试器转变为一系列单元测试,而著名的绿色标语是后者成功执行的代名词。
为了获得最佳实践,TDD必须与良好的工具结合在一起。 像Eclipse这样的IDE必须具有本机JUnit支持。 强烈建议使用插件来促进单元测试的管理,例如MoreUnit和Infinitest。 后者在每次代码更改时自动执行所有单元测试,这减少了反馈周期,这也为连续的单元测试奠定了基础。 另一方面,将代码模板用于单元测试在重复的TDD周期中节省了重要的时间。 在代码级别,对于生成可读和灵活的业务对象,设计模式构建器至关重要。 最后,精通键盘快捷键可以节省宝贵的时间。
就像在武术中一样, 技术的实践可以在片中完成,在片中开发商必须从头解决特定的问题。 网站http://sites.google.com/site/tddproblems/all-problems-1提供了很多问题,这些问题特别适合使用TDD方法解决。 在我们的示例中,我们选择一个程序将阿拉伯格式的数字转换为罗马格式的等效数字。
请注意,您可以在YouTube视频中的阿拉伯数字TDD Kata中找到罗马数字转换器 :
根据TDD方法的要求,我们首先编写RomanNumeralTest类,其中将包含程序的单元测试系列。 后者的第一个要求是输入数字1给出输出罗马数字I,其实现如下:
我们运行第一个测试是为了使其失败(图2)。
图2:TDD周期中的RED步骤如果编译错误失败,我们将继续编写满足此测试要求的生产代码。 为此,我们将自己放在RomanNumeral类所在的行上,并使用Eclipse的键盘快捷键Ctrl + 1提供了“快速修复”功能,以生成空的RomanNumeral类。 然后,我们总是通过此菜单生成此intToRoman方法。 在这个阶段,我们编写最简单的代码来满足单元测试。 在这种情况下,需要在intToRoman方法的输出中返回字符串“ I”:
然后可以成功执行测试,以在TDD周期中继续前进(图3)。
图3:TDD周期中的绿色步骤我们现在进入重构阶段,由于代码中没有重复且没有改进之处,因此重构阶段非常快。 通过添加新测试再次开始该循环:
一旦测试失败,我们将修改RomanNumeral类的intToRoman方法,以成功通过此测试,从而为我们提供以下代码:
随着单元测试变成绿色,我们继续进行重构。 一种方法最好只有一个出口点,并且即使有一条指令,也建议对if条件使用花括号。 这给了我们:
测试保持绿色,我们在数字3的附加要求上进行了扩展,必须将罗马数字III作为输出。 这项新要求使我们的单元测试失败了,有必要编写以下生产代码来对其进行验证:
重构步骤强调了使用循环进行优化的可能性,该循环会减小在输入处传递的阿拉伯数字的值,并因此向输出处呈现的罗马数字添加罗马条:
测试总是成功通过,我们现在对数字10感兴趣,该数字必须在输出中给出罗马数字X:
测试失败后,将编写生产代码。 在这里,我们意识到我们的先前算法没有被修改,因为我们存在一个新的罗马数字X。为解决这个问题,我们添加了一个测试以特定方式处理阿拉伯数字10:
测试正确通过,并且重构阶段不需要任何特殊的工作。 我们添加了一个附加测试,其值为20,必须给出罗马数字XX。 此新要求确实会导致关联的单元测试失败。 然后编写以下生产代码:
此代码足以通过我们的一系列单元测试。 重构阶段使我们可以退后一步,看到可以对包含X的罗马数字优化我们的算法。很容易看出,循环比if / else if更有效。 因此,代码修改如下:
JUnit测试栏始终为绿色,重构未更改方法的外部行为。 对生产代码的研究使我们可以观察到,对于图X和I,所执行的工作基本上是相同的,即,循环递减,这表明分解的可能性。 因此,我们的算法正在出现一条新的设计路径,其中包括引入2个表,这些表的索引分别包含罗马数字和阿拉伯数字。
我们的单元测试验证了这种重构,并保持数字30的正确性。由于不可能用30击败单元测试,因此没有必要更改生产代码。 基于罗马数字X和I,使用数字11和33重复此操作,对于该罗马数字X和I,算法仍可运行。 另一方面,罗马数字V导致关联的单元测试失败。 因此,我们将从完整的TDD周期开始 。 最简单的解决方案是在表中添加罗马数字V及其等效的阿拉伯数字,以检查当前算法是否可以运行。 测试正在绿色进行,就是这种情况。 对于重构, 会出现一个问题,即替换由Java映射链接的2个索引表是否更好? 由于表中包含的值通过2个相互连接的回路按路径降序排列,因此当前的解决方案是更可取的,因为它更简单并且遵循KISS原理 。
我们使用数字4(其对应的罗马数字为IV)完成单元测试。 测试失败,这导致我们修改生产代码以使其通过。 第一种方法是为这种特殊情况添加紫杉,这会使测试变为绿色。 以下重构强调了数字IV本身应被视为符号。 它在数字表中的添加使您可以通过快捷键CTRL + D删除以前添加的紫杉。测试条始终为绿色,可确保程序的外部行为保持不变。
我们程序的TDD开发工作仍在继续 ,我们将逐步完成一系列单元测试(图4)以及包含所有罗马数字及其阿拉伯等效项的表格。
图4:获得的一系列单元测试在大约十个增量之后,这给了我们下表:
ARABIC_DIGITS = {1000,900,500,400,100,90,50,40,10,9,5,5,4,1};
ROMAN_DIGITS = {“ M”,“ CM”,“ D”,“ CD”,“ C”,“ XC”,“ L”,“ XL”,“ X”,“ IX”,“ V”,“ IV” ,“一世”};
使用阿拉伯数字(例如1954或3949)添加新的单元测试最终将不需要对生产代码的intToRoman方法进行任何更改。 由于在TDD模式下进行此开发而获得的单元测试系列为我们提供了最大的代码覆盖率(图5)。
图5:我们程序的代码覆盖率有关TDD的介绍性文章将展示每个开发人员必须在其工具箱中拥有的这种实践的力量。 为了充分利用它,正如我们在示例中所展示的,有必要严格应用其规则。 TDD带来的范式转变涉及开发人员完全投入运作并提高生产率之前的学习阶段 。
基于本文中从阿拉伯数字到罗马数字的转换示例,一个很好的练习是在TDD中执行反向转换方法。 最后,对本文介绍的站点的TDD问题进行评估是最好的改进方法。 简而言之,您将理解它,以便在TDD中取得一个唯一的口号,对任何技术,实践都有效!
要发现开发有效的Java单元测试的好书并了解有关TDD的更多信息,可以阅读以下文章:
发现7本书以开发有效的Java单元测试
如果您搜索用于学习Java编程的最佳书籍,就可以在这里找到我的选择:
学习Java编程的6大最佳书籍
From: https://hackernoon.com/introduction-to-test-driven-development-tdd-61a13bc92d92