关于TDD的思考。 为什么这种方法没有得到广泛认可

哈Ha!

长期以来,几乎没有成功,我们一直在寻找一个能将肯特·贝克先生赶下市场的聪明人-也就是说,我们正在寻找愿意为我们写有关TDD的书的人。 通过真实的例子,讲述您自己的成就和成就的故事。 关于这一主题的书籍很少,您不会对经典著作有任何争议……也许这就是为什么我们还没有见过这个话题。

因此,我们决定不仅要再次提醒自己,我们正在寻找这样的人,还提供了一篇颇具争议的文章的翻译,该文章的作者道格·阿库里(Doug Arcuri)分享了他对为何TDD从未成为主流的看法。 让我们讨论他是否正确,如果不正确,为什么。



这不是通过测试进行开发的介绍。 在这里,我将提出有关重新启动该学科的想法,并讨论单元测试的实际困难。

传奇的程序员Kent Beck是现代意义上的TDD(通过测试开发)方法的作者。 肯特(Kent)和Erich Gamma一起为创建JUnit(一种广泛使用的测试框架)做出了贡献。

肯特(Kent)在他的《 XP解释书》(第二版)中描述了在价值实践的交汇处如何形成原则 。 如果构建概念列表并将其替换为某种公式,则将得到转换。

[KISS, Quality, YAGNI, ...] + [Testing, Specs, ...] == [TDD, ...] 

我对这项工作深表敬意,这对Kent来说是一生的工作-不仅是他在编程中创造的杰作,而且还因为他孜孜不倦地探索信任勇气赠予简单脆弱的本质。 所有这些属性对于极限编程(XP)的发明都是必不可少的。

TDD是XP社区中坚持的一项原则和纪律 。 这门学科已经有19年历史了。

在本文中,我将分享我对TDD如何吸收的看法。 然后,我将分享在TDD会议期间出现的有趣的个人观察。 最后,我将尝试解释TDD为什么没有像看起来那样发难。 走吧

TDD,研究与专业

在过去的19年中,TDD的学科一直是编程界争论的主题。
专业分析师会问您的第一个问题是“今天使用TDD的开发人员所占的百分比是多少?” 如果您向罗伯特·马丁(Robert Martin)(鲍伯叔叔)的朋友和肯特·贝克(Kent Beck)的朋友问这个问题,答案将是“ 100%”。

如果您不通过测试练习开发那么鲍勃叔叔(Bob Uncle)肯定不会认为自己是专业人士

鲍伯叔叔已经紧密地参与了这一学科多年,因此在这篇评论中很自然地关注他。 Bob叔叔为TDD辩护,并大大扩展了该学科的范围。 您可以放心,我对鲍勃叔叔及其务实的教条主义表示最大的敬意。

但是,没有人会问以下问题:“毕竟练习意味着“有意识地使用”,但不允许判断百分比,对吧? 以我的主观意见,即使在任何象征性的时期,大多数程序员都没有处理 TDD。

现实情况是我们真的不知道这些数字,因为没有人积极调查这一百分比。 所有特定数据仅限于在WeDoTDD网站上收集的一小部分公司。 在这里,您可以找到有关此类公司的统计数据,以及一直在实践TDD的公司的访谈,但此列表并不大。 此外,它是不完整的,因为即使是简单的搜索,也可以显示参与TDD的其他大型组织-但也许不是全部。

如果我们不知道有多少家公司采用TDD,那么就会出现以下问题:“根据其可衡量的优点来判断TDD的有效性”?
多年来,您可能会很高兴地进行了许多研究,这些研究证实了TDD的有效性。 其中绝对有来自MicrosoftIBM ,北卡罗来纳大学和赫尔辛基大学的权威报告。



图表来自赫尔辛基大学的一份报告。

这些报告在一定程度上证明了错误密度可以降低40-60%,这需要更多的工作。 运行时间增加了15-35%。 这些数字已经开始在书籍和新的工业方法论中找到,特别是在DevOps社区中。

部分回答这些问题,我们转到最后一个问题:“开始练习TDD时我可以指望什么?” 为了回答这个问题,我提出了我对TDD的个人看法。 让我们继续前进。

1. TDD需要一种口头化的方法

在实践TDD时,我们开始遇到“目标指定”现象。 简而言之,简短的项目(如准备失败和成功的测试)对开发人员来说是严重的智力挑战。 开发人员必须明确指出:“我相信此测试将成功”和“我相信该测试将失败”或“我不确定,请尝试该方法后再进行反思。”

IDE已成为橡胶鸭恳求与她积极交谈的开发人员。 至少,在TDD企业中,此类对话应合并为持续的嗡嗡声。

首先考虑-然后采取下一步。

这种增强在沟通中起着关键作用:它不仅使您可以预测下一步,而且可以激发您编写最简单的代码以确保单元测试通过。 当然,如果开发人员保持沉默,那么他几乎肯定会失去自己的路线,此后他将不得不重返赛道。

2. TDD泵电机记忆

开发人员经历了他的第一个TDD周期后,很快就感到疲倦-毕竟,此过程很不方便,而且经常停顿。 对于一个人刚开始但尚未掌握的任何活动,这是一种常见情况。 开发人员将求助于捷径,尝试优化该周期以填充手掌并改善运动记忆。
马达记忆对于工作的乐趣和发条一样必不可少。 在TDD中,由于重复动作,因此这是必要的。

使用此类快捷方式获取备忘单。 充分利用IDE中的键盘快捷键以使循环有效。 然后继续寻找。

在短短的几个会话中,开发人员可以完美地掌握快捷方式的选择,尤其是几个会话就足以组装和运行测试台。 当您练习创建新的工件,突出显示文本并浏览IDE时,所有这些对您来说似乎都是完全自然的。 最后,您将成为一名真正的专业人员,并掌握所有重构技术:尤其是提取,重命名,生成,引发,重新格式化和下降。

3. TDD至少需要事先对自己的行为进行一点思考

每当开发人员考虑启动TDD时,都需要牢记需要解决的任务的简要思路。 在传统的编程方法中,这样的地图并不总是存在的,并且任务本身可以“在宏级别”呈现或具有研究性质。 也许开发人员不知道如何解决问题,而只是粗略地设想了目标。 朝着这个目标忽略单元测试。

在坐下来工作并以另一个“坐下来”结束时,还应尝试以此做一个仪式。 先思考并列出。 玩吧。 列出更多。 然后继续做,思考。 庆祝。 重复几次。 然后再三思而止。

坚持工作。 跟踪已完成的操作-选中复选框。 直到至少有一个折叠为止。 想想!

列表的措词可能会花费一些时间,这可能不适合工作周期。 但是,在开始之前,您必须有一个列表。 没有它,您将不知道要移动到哪里。 没有卡无处不在。

 //   // "" ->   // "a" ->   // "aa" ->  // "racecar" ->  // "Racecar" ->  //   //    

开发人员应按照肯特·贝克(Kent Beck)的描述列出测试 。 测试列表可让您以顺畅的周期循环的形式解决问题。 即使在测试开始前仅几秒钟,您仍需要不断处理和更新测试列表。 如果测试列表几乎完全减去最后一步通过,则结果为“红色”,整个测试失败。

4. TDD取决于与同事的沟通

上面的列表完成后,某些步骤可能会被阻止,因为它们没有很清楚地描述要执行的操作。 开发人员不了解测试列表。 相反的情况也发生了-列表太粗糙了,其中关于需求的许多假设尚未制定。 如果您收到类似的消息,请立即停止。

没有TDD采取行动可能会导致实施过程过于复杂。 以TDD风格进行工作,但毫无疑问,没有列表,同样危险。

如果您发现测试列表中有空白,请站起来大声说出来。

在TDD中,开发人员必须在解释所有者的必要要求的思想的指导下,了解要做什么产品,仅此而已。 如果在此情况下的要求不清楚,则测试列表将开始崩溃。 此故障需要讨论。 冷静的讨论很快有助于建立信心和尊重。 此外,这就是形成快速反馈环路的方式。

5. TDD需要迭代架构

早在XP著作的第一版中,肯特(Kent)就建议测试应该成为架构背后的推动力。 但是,在过去的几年中,出现了一些关于短跑团队如何在数个短跑中已经绊倒在墙上的故事。

当然,基于测试构建体系结构是不合理的。 鲍伯叔叔本人也同意其他专家的说法,这不好。 需要更广泛的地图,但距离您在“现场”开发的测试列表不太远。

多年以后,肯特(Kent)在他的《 TDD范例》一书中也表达了这一论点。 竞争力安全性是TDD不能成为驱动力的两个主要领域,开发人员必须分别处理它们。 可以说,竞争力是系统设计的不同层次,竞争力需要通过迭代来开发,并将此过程与TDD协调起来。 在今天,尤其是这样,因为某些体系结构正在朝着反应性范式和反应性扩展发展( 反应性是其顶峰时期的竞争力)。

绘制整个组织的更大地图。 帮助从一些角度看事物。 确保您和团队沿同一方向前进。

但是,最重要的想法是整个系统的组织 ,并且没有提供一个TDD组织。 事实是,单元测试是一个低级的东西。 迭代架构和TDD编排在实践中很复杂,并且需要所有团队成员之间的信任,结对编程和可靠的代码审查。 目前尚不清楚如何实现这一目标,但是很快您就会发现,应该与主题领域中的测试列表的实现保持一致地进行简短的设计会议。

6. TDD揭示了单元测试的脆弱性和退化的实现

单元测试具有一项有趣的功能,而TDD完全可以将其放弃。 他们不允许证明正确性。 迪克斯特拉(E.V. Dijkstra)致力于解决这个问题,并讨论了如何在我们的案例中进行数学证明来填补这一空白。

例如,在下面的示例中,解决了与业务逻辑所规定的假设不完全回文有关的所有测试。 使用TDD方法开发了一个示例。

 //    @Test fun `Given "", then it does not validate`() { "".validate().shouldBeFalse() } @Test fun `Given "a", then it does not validate`() { "a".validate().shouldBeFalse() } @Test fun `Given "aa", then it validates`() { "aa".validate().shouldBeTrue() } @Test fun `Given "abba", then it validates`() { "abba".validate().shouldBeTrue() } @Test fun `Given "racecar", then it validates`() { "racecar".validate().shouldBeTrue() } @Test fun `Given "Racecar", then it validates`() { "Racecar".validate().shouldBeTrue() } 

确实,这些测试中存在缺陷。 即使在最琐碎的情况下,单元测试也很脆弱。 永远不可能证明它们的正确性,因为如果我们尝试这样做,将需要进行令人费解的脑力劳动,而为此所需的投入将无法想象。

 //   ,      fun String.validate() = if (isEmpty() || length == 1) false else toLowerCase() == toLowerCase().reversed() //   ,    fun String.validate() = length > 1 length > 1 

length > 1可以称为简并实现 。 解决任务就足够了,但是它本身并没有报告有关我们要解决的问题的任何信息。

问题是-开发人员何时应停止编写测试? 答案似乎很简单: 从业务逻辑的角度来看,这已经足够了 ,而不是根据代码的作者。 它可能会损害我们的设计热情 ,而简单性会激怒人们 。 看到我们自己干净的代码并理解以后可以放心地重构代码,这些满足感就会得到满足。 所有代码将非常简洁。

注意,对于所有的不可靠性,必须进行单元测试。 了解他们的优点和缺点。 如果没有完整的图片,也许这个空白将有助于填补变异测试

TDD有其好处,但是这种方法可以分散我们对不必要的沙堡的建造的注意力。 是的,这是一个限制 ,但是有了它,您可以更快,更远,更可靠地移动。 鲍勃叔叔在描述从专业角度讲意味着职业的想法时,可能就是这样

但是! 不管单元测试对我们来说多么脆弱,它们都是绝对必须的。 是他们将恐惧变成了勇气 。 测试提供了温和的代码重构; 此外,它们可以作为任何新开发人员的指南文档 ,只要他们可以在单元测试中很好地参与项目开发,他们就可以立即开始为项目的利益而努力。

7. TDD演示了测试语句的反向循环

更进一步。 为了了解以下两种现象,我们研究了奇怪的重复事件。 首先,让我们快速看一下FizzBu​​zz。 这是我们的测试清单。

 //    9  15. [OK] //  ,  3,  Fizz  . // ... 

我们向前走了几步。 现在我们的测试失败了。

 @Test fun `Given numbers, replace those divisible by 3 with "Fizz"`() { val machine = FizzBuzz() assertEquals(machine.print(), "?") } class FizzBuzz { fun print(): String { var output = "" for (i in 9..15) { output += if (i % 3 == 0) { "Fizz " } else "${i} " } return output.trim() } } Expected <Fizz 10 11 Fizz 13 14 Fizz>, actual <?>. 

自然地,如果我们在assertEquals复制期望的断言数据,则可以达到期望的结果,并执行测试。

有时,失败的测试会给出通过测试所需的正确结果。 我不知道这类事件的名字...也许是伏都教测试 。 您碰巧看到这种情况的次数-部分取决于测试时的懒惰和礼节,但是当一个人试图获得一个可以正常使用现成的和可预测的数据集的实现时,我已经注意到很多次。

8. TDD显示了转换顺序

TDD会困住您。 碰巧的是,开发人员对自己进行的转换感到困惑,他将其用于实现所需的实现。 在某些时候,测试代码变成了我们停滞的瓶颈。

死胡同形成。 开发人员必须退后并撤防,删除一些测试才能摆脱陷阱。 显影剂仍然不受保护。

鲍勃叔叔在他的职业生涯中很可能陷入这种僵局,此后,他显然意识到,为了通过测试,您需要设定正确的行动顺序,以最大程度地降低陷入僵局的可能性。 此外,他还必须意识到另一个条件。 测试越具体,代码就越通用



转换顺序。 您应该始终争取最简单的选择(在列表顶部)。

这是过渡优先条件 。 显然,存在一定程度的重构风险,在通过测试后,我们已经准备好进行重构。 通常,最好选择显示在列表顶部(最简单)的转换选项-在这种情况下,进入死胡同的可能性仍然很小。

可以说,TPP或Bob叔叔的测试分析是目前观察到的最有趣,最技术和最令人兴奋的现象之一。

使用它可以使您的代码尽可能简单。
打印出TPP列表并将其放在您的桌子上。 和他一起检查,避免陷入僵局。 使其成为规则:订单应该很简单。

这总结了我主要观察的故事。 但是,在本文的最后部分,我想回到我们一开始就忘记回答的问题:“今天使用TDD的专业程序员所占的百分比是多少?” 我会回答:“我认为他们很少。” 我想在下面调查这个问题,并尝试解释原因。

TDD是否在实践中根深蒂固?

不幸的是,没有。 从主观上讲,它的支持者比例似乎很低,我将继续搜索数据。 我在招聘,团队领导和自我发展方面的经验(令我着迷)使我可以进行以下观察。

原因1:缺乏与真实测试文化的联系

我可以合理地假设大多数开发人员没有机会在真实的测试文化中学习和工作。

测试文化是开发人员自觉地实践和改进测试技术的环境。 他们不断培训仍在该领域没有足够经验的同事。 每对配对和每个汇总请求都建立了反馈,可帮助所有参与者发展测试技能。 此外,在工程师的整个层次结构中都提供了认真的支持和肘部感觉。 所有管理人员都了解测试的本质,并对此深信不疑。 当截止日期开始用尽时,测试纪律不会被丢弃,但会继续遵循。

例如,那些幸运地在像我这样的测试文化中进行测试的人,就有机会进行类似的观察。 .

2:

TDD, , xUnit Patterns Effective Unit Testing . , -, , , . .

. , . . , , , … .

3:

: , , . , , ; - , – .

4:

, , TDD . , .

, , : « , ». : , « » — .

– .

结论

XP – , . – , . TDD.

, , . «» , , – , .



XP Explained. , , .

, - .

, – , . , , , .

, , , .

TDD « » , . TDD . TDD .

. TDD , . TDD — , , . , TDD , . , .

 @Test fun `Given software, when we build, then we expect tests`() { build(software) shoudHave tests } 

, TDD – , , . . , , , .

Source: https://habr.com/ru/post/zh-CN427853/


All Articles