跳过主要内容

保存的文章

几乎,但不是完全,完全不同于TDD

Dave Nicolette |领导敏捷
戴夫尼科尔特
读: 几乎,但不是完全,完全不同于TDD

几乎,但不是完全不同于测试驱动的发展

道格拉斯·亚当斯”Hitchhiker的Galaxy指南曾经是软件开发人员的必读书籍。啊,过去的好时光,当开发者分享某种文化素养的时候!也许是一种奇怪的类型,但无论如何也是一种类型。任何一组开发人员都可以根据要求背诵故事中的引言,或者在工作场合可能会引起恐慌的情况下,如果不是因为指南封面上的安慰语:“不要恐慌。”像这样的引用:

“(自动营养机)的运作方式非常有趣。喝按钮被按下时的瞬间但非常详细检查主体的味蕾,光谱分析的主题的新陈代谢,然后发出了微型实验信号的神经通路的味道中心主题的大脑,看看可能会下降。然而,没有人知道它为什么会这样做,因为它总是提供一杯几乎不像茶的液体。”

年轻的软件开发专业人员对塑造早期实践者思维的经典科幻小说和喜剧材料知之甚少。很少有人能背诵出巨蟒剧团的对话,或者仅仅通过命名一个“模糊地带”情节就能简洁地传达问题的显著特征。

它会毫不奇怪,这有时会导致应用强大的软件开发技术的问题。

例如,我注意到许多软件开发人员声称是测试驱动的开发(TDD)的倡导者,并坚持他们在自己的工作中使用TDD,但它们构建代码的方式几乎是完全不同的,完全不同于TDD。

无论如何是什么TDD?

如果你问10个人TDD是什么意思,你可能会得到30个不同的答案。大多数答案在内部是一致的,其中一些是实用的。有些甚至在某种程度上类似于TDD。

我将分享我对TDD的理解。YMMV。但首先让我讲一讲另一件事。

紧急设计

有一种软件开发方法,通过这种方法,我们逐步发展代码的低级设计。当代码成形时,它本身就“告诉”我们应该如何设计它,如果我们愿意的话。

用这种方式听代码需要训练有素的耳朵。我们必须学习如何听代码,就像我们必须训练我们的耳朵听音乐或外语一样。

或许我们应该训练一下我们的鼻子。人们喜欢谈论的话题代码气味.代码气味是源代码中的一种结构模式,它使我们怀疑设计还可以改进。它不一定意味着有一个设计问题;这只是一个可疑的模式经常指出了一个设计问题,就像你房子里不寻常的气味可能意味着危险的气体泄漏,或者没有什么比你邻居的烹饪更可怕的了。

如果我们不太确定这些模式是什么样子的,我们也不会太确定代码试图告诉我们什么。

如果我们不知道密码想告诉我们什么,我们就不知道是哪个重构用来改进设计的。

我经常和一些开发人员一起工作,他们可以盯着用Java或c#编写的2000行方法而不感到焦虑。密码在说话,但他们听不到信息。

这让我想起和我的狗狗一起听音乐。播放莫扎特的音乐时,她平静地躺下。她好奇地调查着说话的人Yasuhiro Yoshigaki在旧汽车零件上即兴发挥听到乔治·克拉姆的声音,她惊恐地逃跑了黑色的天使.但在任何情况下,她都无法与音乐产生深层次的共鸣。有时这些声音会激发她的反应,但她不懂音乐。我敢打赌,她对Java或c#中包含2000行代码的方法根本不会有任何反应。

不管怎样,让代码告诉我们它想要的样子,并让设计渐进地发展,这种事情通常被称为紧急设计

你可以谷歌这个短语。去做吧。我将等待。

你可能在维基百科上找到了这篇文章:紧急设计,这表明该术语不限于软件开发,但应用更广泛。

与软件开发相关,你可能会发现这篇文章从思考作业,紧急设计提倡者的意见像这个和对方法的批评像这个.因此,当它可能有用时,您可以了解它意味着什么,并且当它可能无用时。都好。

让我们将支持和反对紧急设计的争论放在一边,并将其作为本文的“给定”。我们将如何指导软件模块或组件的低级设计的出现?您可能会想到几种方法来做到这一点。最常用的方法是测试驱动开发

TDD作为一种指导紧急设计的方法

在这个上下文中,我们讨论的是构建软件解决方案的小规模组件。我们通过在非常有限的范围内以可执行形式表达代码段所需行为的具体示例来实现。这些小例子叫做微量

TDD循环——红色、绿色、重构——用于驱动代码的期望行为的实现。“红色”意味着期望行为的可执行语句没有显示出预期的结果。“绿色”的意思是它这样做。这些单词反映了单元测试工具通常表示失败和通过的例子的颜色。“重构”意味着清理代码,我们更喜欢增量地做,而不是建立大量的技术债务,这样任务就不会变得繁重,代码始终保持在可理解的状态。

从非常简单的示例开始,随着微测试套件的构建,一个适当的低级设计就出现了。该方法的著名支持者罗伯特“鲍勃叔叔”马丁(Robert“Uncle Bob”Martin)将其作为示例进行解释变得更具体,实现变得更加通用.换句话说,随着我们添加越来越多的离散示例,我们将被引导编写越来越通用的实现,能够正确地处理所有定义的案例。

TDD的许多批评者指出,人们可以忘记包括所有相关例子,导致脆弱或不完整的实施。这更像是人们忘记了对TDD或任何其他技术或方法的客观批评的问题。毕竟,软件不会对我们的思考。好吧,不是然而,无论如何。

鲍勃叔叔列了一张转换代码随着设计的出现而发生变化。通过偏爱最简单的转型必须导致Microtest通过,我们可以指导紧急设计朝着适当的形式。

最好还是足够好?

我不愿断言我们最终得到了“最佳”设计,因为这将涉及以各种可能的方式编写解决方案,然后根据一些每个人都同意的标准来判断各种实现。你可能会发现这个想法存在一些挑战。第一个挑战是思考每一种可能的执行方法。

在发现任何软件解决方案的“最佳”设计的过程中,我还没有遇到过超越第一个挑战的人。如果存在这样的人,那么他们将面临第二个挑战:让每个人都同意确定“最佳”设计的标准。因此,我怀疑任何人知道任何给定的解决方案的“最佳”设计可能是什么,即使有些人也是如此相信他们是这样。

Short of absolute perfection, I’m pretty happy with having a practical way to discover an appropriate and practical design that doesn’t go too far (that is, helps me avoid overengineering) and doesn’t overlook anything important (that is, helps me think of significant edge cases). I’ve found TDD helpful in those ways. YMMV.

TDD不管叫什么名字,闻起来都一样甜

好吧,这是一个非常长的啰嗦回答“无论如何是什么?”我要说的是,在转换的逻辑进展之后,TDD意味着(对我来说)以非常小的增量重复红绿笔重构周期,以指导软件的实用和适当的低级设计的出现组件而不忽略重要边缘案例,并通过增量重构保持设计“清洁”。

关于这一切的一个关键点是确保编写一个定义行为的微测试你实施这种行为。TDD中间的D代表“驱动”。汽车的司机坐在前面座位,而不是后方。(Once when I used that metaphor, a person in the room showed me a picture of a car that had been rigged for back-seat driving. Clever.) Anyway, it’s fundamental to TDD that the only reason to write a line of implementation code is to make a red example turn green. That’s kind of hard to do if you’ve already written the implementation before you write the example.

如果你在做一些不同的事情,也没关系。没有法律规定我们必须以任何特定的方式开发软件。问题是,当你没有做我刚才说的那些事情时,就把你正在做的事情称为“TDD”。这就像把1948年福特皮卡上的化油器叫做“香蕉”一样没有意义。

这是一个if-P-then-Q-doesn不是暗示风格的推论,以甜蜜的断言:

任何叫TDD的东西闻起来都不太好

这些年来,我遇到过不少开发人员,他们坚持认为自己是TDD的坚定支持者和专门实践者。他们首先在纸上(或像素上)列出相当详细的低级设计。然后他们编写一堆“骨架”源模块。最后,他们使用红绿重构循环来帮助自己填补骨架源模块中的空白。或者他们使用一种“绿色-绿色永不重构”的循环,出于某种原因,他们将其称为“TDD”。

本段的其余部分包含一些读者可能会发现令人不安的材料。随意跳过它,或者让孩子们在阅读时离开房间。不要说我没有警告过你!Here goes: On many occasions, I’ve witnessed experienced TDD practitioners demonstrate or teach TDD by making hard-and-fast assumptions about how the solution is destined to emerge, and to begin by writing some “initial” production code before they write the first microtest. In Deccember I saw a demonstration of the保龄球型其中,协调人首先为Game和Frame创建了c#类,为Roll创建了一个空方法。他明目张胆地做了这一切,却没有编写一个微测试来驱动它。很抱歉读到这让你心烦意乱,但我不得不说。好了,现在可以邀请你的孩子们回到房间了。

最近,我已经了解到了一个关于发展人员称之为“反向TDD”的思想学校或者对此效应的话语。如果你问他们,他们会经历一个相当长的独白来描述它。有时候他们会这样做,即使你不要求他们。或者如果你要求他们不要。“反向TDD”基本上意味着在编写实现后写入单元测试。不确定缩写如何与之合作,但在那里你有它。

如果道格拉斯亚当斯仍然存在并观察这些活动,他可能会很好地称之为“几乎,但不完全,完全不同于TDD”。

为什么?

为什么人们觉得有必要将任何旧的伪随机方法标记为软件开发“TDD?”为什么这个术语对他们来说意义重大?如上所述,有很多方法来编写代码,而且它们都不是“错误”或“邪恶”。只要你对你的工作和自己的工作感到满意,那就很好了。有些方法可能需要更多时间或携带更高的过度或错误的风险,但最终,给予足够的时间,金钱和挫折,a工作方案可以用很多来生产吗任何方法。所以,为什么要坚持致电“TDD?”致电

如果让我猜的话(我,正如我不是心理学家),我猜这个现象的一个原因是TDD这几天是非常受欢迎的嗡嗡声。每个人都喜欢与流行的嗡嗡声相关联。因此,“无论我该做什么都可以被标记[插入 - 流行的嗡嗡声 - 这里]因为我这么说。”

即使我的猜测没有错误(它可能是),它也没有完全解释这些几乎但不是完全完全不同于TDD的TDD。问题并不完全是由于开发人员对技术的误解或他们渴望获得受欢迎的标签。许多教程和TDD的解释明确地建议开发人员在编写失败的Microtest之前编写生产代码。来自Microsoft的此示例是代表性:开始测试驱动开发

但即使是可以用我对心理动机的猜测来解释。有东西要“销售”的公司和其他公司都非常喜欢与流行术语联系在一起。他们可能理解也可能不理解这些术语的意思。他们了解人们将购买与流行的嗡嗡声相关联的东西。

那么,我认为我在那里都有所有这些漫无主义的废话?就是这样:事情有名字和定义。如果您更改了这种程度,这是其基本特征不再符合其定义,那么您真的应该提出一个新名称。事情不再是什么。通过旧名称呼叫它只会混淆实际知道旧名称的人。

即使你皱着眉头,摆出一副教授的样子,他们也会知道你没有正确地使用这个术语。相信我。我已经试过。

TDD的变化

我声称,教制性地,任何与这些规则的偏差使标签“TDD?”无效。不可以。TDD至少有几个众所周知的变化,仍然满足基本标准。

经典的TDD遵循上面描述的模式。当我们需要为任何类型的算法实现生成一个低级设计时,这是非常有用的。经典的TDD也被称为底特律TDD学校,因为它由在底特律市工作的人设计。这是肯德斯·鲍勃,罗恩·杰弗里斯做TDD的方法(并不是他们知道的唯一方法)。微测试往往不知道实现细节,而是关注被测代码单元的可观察输出。

Mockist风格的TDD采用的方法是为开发中的解决方案的关键领域概念定义接口,并使用红绿重构周期构建解决方案,并为测试中的代码协作的组件定义模拟。在开发以域对象之间的许多交互为特征的解决方案时,它非常有用。Mockist风格的TDD也被称为伦敦TDD学校它是由在伦敦工作的人设计的。这是纳特·普莱斯,史蒂夫·弗里曼做TDD的方法(并不是他们知道的唯一方法)。测试用例倾向于了解底层执行的更多信息,而不是使用经典风格TDD,至少在与协作对象的交互程度范围内。

TDD的实践者经常在这些风格之间切换,并采取他们建议学生不要采取的捷径。你很少会看到有人拘泥于一种风格。建议初学者采取极端程度的婴儿步骤,以便他们能够内化技术,并对它如何影响紧急设计有一个直觉。一旦超过了最初的学习阶段,就可以更加灵活。最好紧紧跟随这些步骤,直到你知道你可以安全弯曲多远。

有时候初学者会错误地认为向他们展示TDD的实践者希望他们永远保持僵化。事实并非如此。这是一种学习的方式。不要跳过它。如果你是TDD的初学者,那么你并不知道什么时候和多远的伸缩是安全的。

当一个变奏完全变成另一首歌

我提到的嘲笑者风格的东西......它听起来很像写骨架源模块,然后在使用红色的绿色重构周期时填写它们,不是吗?我想知道这是否指出了概念之间没有艰难的边界线;人们可能会有灰色的地区或机会申请判断。唔。

事实证明,即使我们想在解决方案的某些方面使用紧急设计,我们也不会经常使用它全部方面。解决方案的部分可能是众所周知的设计模式或参考架构的直接示例。假装的价值有限,我们对他们无所了解并强迫自己为每一件小事开除一个紧急的设计。

此外,紧急设计通常不会意味着根本没有预先设计,除非我们正在尝试或了解我们不熟悉的域。在建立用于生产的代码时,我们通常会执行一定数量的前滨设计。

一个符合敏捷宣言一致的轻量级开发方法特性驱动的开发(FDD).出于FDD社区的嗡嗡声是JEDI,或者最初的设计要足够.另一种流行的设计方法被称为域驱动设计(DDD),由埃里克·埃文斯设计。Scott Ambler定义了他调用的另一种轻量级设计方法敏捷建模

所有这些以及更多方法都可用于详细说明高度详细的域模型或非常轻的模型。这取决于用户。即便是统一建模语言(UML)和Rational统一过程(RUP)可用于生产全面的前置设计或最小的一个。

理想情况下,我们想在中间找到最佳位置,在正面的前面设计上,自上而下,紧急设计的开始自下而上。最佳地点会因上下文而有所不同。了解上下文取决于我们,不是方法或工具的问题。

也许有些人坚持将他们所做的一切都称为“TDD”的另一个原因是,他们认为他们必须对所有工作使用一种单一的方法,他们正在寻找一个总称术语。TDD并不是他们想要的。它只意味着它的意义。它不代表它不代表的意思。

在给定的环境中结合不同的技术来实现我们的目标并没有什么错。最近,我发现了将伦敦学校的TDD与另一种技术结合起来的价值合同设计作为一种为云环境开发微服务的方法。对于驱动个人微服务的设计,我喜欢使用底特律学校的TDD。我很乐意使用框架和库来制作样板文件。每样东西都有它的位置。

练习足够好

TDD是一种习得的技能,我们通过有意识的练习来提高习得的技能。

但是…什么?只是“足​​够好?”不是,“练习完美无缺?”

如果老话说得对完美是善良的敌人。逻辑上是这样的“足够好”比“完美”要好。因此,如果你是一个完美主义者,你应该瞄准“足够好”。针对“完美”将使您定义为您提供一个不太完美的完美主义者。(诺曼,坐标。)

代码道琼和其他的动手实践活动是一个很好的方法,可以让实践者学习和试验不同的软件开发方法。大多数编程型都可以用不同的方法来实现,并且可以用来比较和对比给定不同假设集的不同方法。

就个人而言,我喜欢在我的工具箱中拥有多种工具,并培养何时使用每个工具。实践练习没有替代,以了解各种技术,并获得何时和如何应用它们的意义。

离别词

这并不是一个关于TDD的速成课程。我只是想说,TDD意味着它的意义,而不是它的不意义。我想在这方面它有点像其他很多单词和短语;至少,是那些言不由衷,言不由衷的人。

你不需要把你做的每件事都称为“TDD”,只是为了听起来更与时俱进。TDD是(或者可以成为)您的工具包中的一个工具。如果您在编写示例之前编写实现代码,那么无论如何,您都没有“改进”、“扩展”或“适应”TDD。它不是TDD,就像化油器不是香蕉一样。

下一个;谁应该成为ScrumMaster?谁应该成为产品负责人?

自1977年以来,Dave Nicolette一直是IT专业人士。他在各种技术和管理角色方面服役。自1984年以来,他主要担任顾问,在管理营中保持一英尺。

评论(2)

  1. 布莱恩按钮
    回复

    我喜欢这篇文章,戴夫。我确实从2016年从显然不喜欢TDD和紧急设计的人那里阅读这篇文章。当我读到他的每一点时,我的第一次反应是我永远不会这样做:)

    大约一个月前,我写了一篇类似的文章:https://blog.brianbutton.io/index.php/2018/04/01/the-degrees-of-unit-testing/.这是一次尝试定义“测试后”、“测试先”和“测试开发”。

    ——巴布

    回复

留下你的评论

您的电子邮件地址不会被公开。必需的地方已做标记