TDD – 如何真正开始思考TDD?
我一直在阅读关于敏捷,XP方法和TDD的内容。
我一直在项目中指出它需要做TDD,但是大多数testing都是以某种方式进行集成testing,或者在项目过程中TDD被忘记了,以尽快完成代码。
所以,就我的情况而言,我写了unit testing,但是我发现自己会先写代码,而不是写testing。 我觉得有一个思想/devise/范式的变化实际上是巨大的。 所以,尽pipe人们真的相信TDD,但是实际上,由于时间压力/项目可交付成果,您最终会回到过去的风格。
我有几个class,我有纯粹的unit testing代码,但我似乎无法继续这个过程,当嘲笑进入图片。 另外,我有时会看到:“为它写一个testing”并不是太简单。
你们觉得我应该怎么处理呢?
我觉得很有意思的是,到目前为止,没有任何回应触及了我认为是对现代开发实践的基本认识,那就是通过收集需求来编写软件的“老式”方式,进行所需的分析和build模系统在编写任何代码之前实际上已经有很多事情要做。
TDD实际上在一定程度上体现了这一点。
要写一个testing你首先必须知道的 – 把事情放在非常简单的条件下 – 你的input是什么以及你的期望输出是什么。
一旦你有了这些知识,你可以编写一个testing来练习一些神秘的代码,在某种程度上,还有什么其他的代码将会与你的代码交互,然后你自己编写代码或者创build这些工件。
这就是我们以前在旧的“瀑布”方法中所称的“需求工程”和“系统分析”。
除此之外,你会发现一旦你掌握了这个级别的需求,写作testing就会自然而然地发生(毕竟,这只是在这些需求中体现的function声明的代码expression)。
并且在以testing的forms编写expression需求的代码之前,您将在以可执行代码的forms将这些差距和误解落实到项目之前,找出需求中的差距和误解。
对于“敏捷”方法的现代从业者承认他们参与了一系列的“瀑布”,我觉得太尴尬了,所以这种对需求工程和理解的需求被语言背后的混淆所困扰。同时拼命地避免承认“敏捷”(通常被理解,或者被误解)把婴儿扔出去了许多洗澡水。
所以,就我的情况而言,我写了unit testing,但是我发现自己会先写代码,而不是写testing。 我觉得有一个思想/devise/范式的变化实际上是巨大的。 所以,尽pipe人们真的相信TDD,但是实际上,由于时间压力/项目可交付成果,您最终会回到过去的风格。
您可能想要尝试在一个固定的时间内对TDD进行绝对的纪律处理 – 比如说几个星期或一个月。 当您在testing之前自己写代码时,将其删除并重新开始,并且testing失败。 做到这一点,直到你知道你可以,直到你知道是什么感觉 – 然后你有能力做到这一点,并有自由作出明智的select – 并明白,如果这种select有时编码第一。 但不要让习惯把你从好的做法中解救出来。 首先得到良好的实践,然后select何时应用它们(当然,您也可以find答案是“永远”)。
我有几个class,我有纯粹的unit testing代码,但我似乎无法继续这个过程,当嘲笑进入图片。 另外,我有时会看到:“为它写一个testing”并不是太简单。
嘲笑是一回事 – 你还擅长吗? 如果你对嘲笑感觉不舒服,而你处于嘲笑状态,那么就练习,直到像TDD一样,你对他们很好。
关于“太小”的问题 – 不是头几个星期。 只是TDD一直。 而当你发现TDD正在推动你更好的devise – 即使是“太小”的代码 – 注意它。 考虑一下你的代码没有它的样子。 而且你可能会发现没有代码对于TDD来说太微不足道了。 或不。 但是我的观点是,认真地尝试一段很长的时间,然后你可以做出更好的select。 获得舒适,然后评估。
这很简单:
By learning to think about the "What" __before__ you think about the "How"
换句话说,想想你想做什么(接口),而不是你如何去做(实现)
TDD作为一个devise工具,根据我的经验,从编码器的angular度来看,真正帮助您从用户的angular度来看待事物。 另外,我认为TDD可以帮助你的头脑真正地思考你正在做什么,期望的结果是什么等等。
所以下一次当你尝试“TDD”的时候,问自己你在做什么,然后开始写代码来expression你的意图。
例:
说,有人希望你写一个整数加法器。
最没有TDD的人只会做:
int add(int a, int b) { return a + b; }
上面的代码是否正确? 就是这样。 但是,根据我的经验,这种方法在您编写复杂的组件时会失败。 (这就是为什么你首先提出这个问题的原因;我知道,我曾经在那里(也许还是?))
关于TDD的好处是,它迫使您首先关注系统的接口(什么),而不是立即要求您执行(如何)。
换句话说,如果有人要求我写一个加法器,我会有这样的东西:
void assertOnePlusTwoEqualThree() { assert( add(1,2) == 3 ); }
注意,甚至在考虑add()应该如何工作之前,我已经解决了一些问题。 那就是我已经想通了:
- 我的加法器的input和输出接口
- 第一个简单的testing用例(unit testing免费!!)
那么你实现了add()。
看,这里讨论的逻辑是不是很容易实现。 有一个TDD的思维方式,就是一直应用它,毫无例外。 你必须这么做很多次,你甚至不会再去想它了。 这只是你devise的一部分。 我可以这样说,因为我看到它发生在我的专业(花了大约一年的毅力)。
就像如果你总是在编程方面做得很好,不pipe手头有多复杂,你都以同样的方式来处理你的工作。
最后,我认为TDD与“代码草图”相当。 也就是说,你开始尝试看看每个场景(testing用例)的界面是否工作正常。 所以,如果最终改变了界面,名称等等,那就没问题了。看,我所学到的很多时候,devise只是彻底地理解问题。 TDD是一个工具,可以让你做到这一点。
人类的头脑,国际海事组织,具体的例子(testing案例/场景),而不是抽象思维(如何实施)更容易工作。 通过testing案例,它可以让您的头脑逐渐了解您正在尝试解决的问题。
不知道(回到“传统”的方式…放松,让你的思想调整)。 如果它不是完美的(写一些实现代码是完全没问题的,那么你的大脑只是想弄清楚什么)。 只要不断尝试,继续阅读,继续编码,但永不放弃! =)
“所以,虽然人们真的相信TDD,但是由于时间压力/项目可交付成果,你实际上最终会回到过去的风格。
其实,我不认为这可能是真的。
如果你“回老式”, 唯一的原因可能是因为你不相信TDD会更快地产生更好的代码。
我认为有人会停止使用TDD,因为他们认为更好地生成较差的代码更快。
学科。
做出决定使用TDD并坚持下去,完全是你自己承担一个过程的能力,并看到它的结论。
尝试练习代码卡塔 。
卡塔是为了记住。 一个卡塔的学生把它当作一种forms来学习,而不是一个结论。 重要的不是卡塔的结论,而是导致结论的步骤。 如果你想倾向于思考我的想法,devise我的devise方式,那么你必须学会如何反应我的反应细节。 以下表格将帮助你做到这一点。 当你学习这个forms,并重复它,并重复它,你将调节你的思维和身体,以回应我对devise决定的微小因素的回应。
代码卡塔给了我一个TDD应该是什么感觉的感觉。 当我开始放松节奏时,我早已知道,回到旧习惯。 在这一点上,我知道我需要采取更小的步骤,更频繁地运行testing,确保我不想在红色下重构等等。
购买Kent Beck的“ Test Driven Development:By Example ”,并阅读它。
然后,写一个失败的unit testing。
琐碎的代码是错误隐藏的地方。 我认为主要是因为阅读那里的东西,而不是那里的东西,这是人的本性。
我一直在试图训练自己在所有新项目上跟随TDD – 这是很难打破旧习惯的,但我认为值得。 由于有更多的前期编码,这似乎需要更长的时间,但debugging时间已经缩短。
不要考虑在编写代码时如何testing代码。 编写testing代码应该如何工作,然后写最简单的事情,使testing通过。 泡,冲洗,重复。 你的代码可能最终会变成你想象中的那样,但肯定会更好。
另外,重要的是,写你的testing失败。 编写testing通过不会帮助find缺陷。 这是编写testing的另一个很好的理由 – 如果你先编写代码,然后testing,testing将被写入代码传递,并且(无意)偏向testing代码而不是期望的结果。 你怎么知道你的testing在没有正确的代码的情况下会失败呢? 由于testing只是代码,它们也容易出错。
所以先写你的testing,然后编码,一点一点的并行。 使用testing来断言你的代码是正确的,并使用代码来声明你的testing是正确的(是的,它可能发生,你确定你的algorithm是完美的,但testing仍然失败,所有,因为你犯了一个愚蠢的错字在testing中,我今天早上做了)。
你们觉得我应该如何处理?
我build议你认为你的unit testing与对象的对话(假设你正在使用OO启用的语言,如Java,C#,Ruby,PHP等)进行编程。
创build一个testing用例类,在你的testing方法中考虑你想要与对象进行的交互。 想象一下,您正在使用传递给对象的方法作为动词来与智能机器对话,然后使用Asserts检查对象是否以您希望的方式作出反应。
过了一段时间,你可能会喜欢这种风格的程序,所以你不会期望没有它编程;-)
配对计划做TDD(理想情况下有人比你更好),
即使是在你的空闲时间
TDD是其中的一种技能,至less在我看来,它比我想像的更糟糕。 当我参与TDD项目时,我的TDD技能总是会提高,并且可以将编程与能够更好地进行TDD的人联系起来。 即使是与不了解它的人进行配对编程,也比单独工作更能帮助我充实自己的想法。
查找(或启动) 代码撤消或编码dojo ,特别关注对编程和TDD。
如果你的公司允许你,在公司时间或至less在公司现场组织一次,即使每周只有一个小时,或午餐时间。
TDD首先不是写testing。 如果你想要的只是先写testing,那么你就需要testing优先技术。 问题在于,真空中的“testing先行”技术(没有TDD)最终会衰减成比几乎没有testing更差的东西。
你说你自己的问题是:
或者在项目过程中,TDD被遗忘以尽快完成代码
然而,如果你正在做TDD,并且认识到它提供的所有价值,那么你的团队就会明白, 没有 TDD就不可能更快地完成代码,因为它是使编程过程快速的原因。
我的build议是花一些时间,通过认识一个好的testing可以取代的文物的种类来理解它可以给你的价值。 通过真正的TDD,单个文档可以满足以下所有angular色/需求:
- testing(这是否工作?)
- 分析(我们试图完成什么?)
- devise(我们将如何完成?)
- 规格(我们如何知道什么时候完成?)
- 终点线(嘿,我们完成了!)
- 进展报告(嘿,他们完成了吗?)
- 过程控制(接下来我要做什么?)
…等等。 我认为,致力于学习TDD – 这是一门硬性的学科,需要大量的实践才能掌握 – 需要清楚地了解实际的奖金,
一旦你清楚地知道了这些事情,你就可以开始担心如何做TDD了。
当你在遗留代码大乱,我发现工作有效的遗产代码非常有用。 我认为,即使是在对旧的遗留代码进行任何更改之前编写unit testing,也会提高TDD分配的动力。 从你的问题的低调看来,这是你所处的位置。
当然还有许多人指出纪律 。 过了一会儿,迫使你自己,你会忘记你为什么以另一种方式做了。
TDD确实是一个很好的做法(但同时我认为我们不应该试图达到100%的代码覆盖率)。
其主要优点是:
-
它使得开发人员思考API,因为他需要首先使用该API来编写testing。 在这一步,很容易感觉到你的API出错了(坏的方法的名字,对于大多数常用操作需要很多行,太多的奇怪的检查exception或反向用户不可能处理可能的exception)。 你可以改进它,只要你喜欢,因为没有人使用它。
-
你想想你的方法应该如何工作。 这往往会在早期步骤中引发一些隐含的问题或误解。
-
当你有testing时,你有一个很好的方法来衡量完成工作的百分比(例如,如果你有20个testing,其中15个通过,那么75%的工作已经完成)。 作为一名开发人员,你对团队或stream程来说更重要。 你把整个任务分成许多小任务(比如20个),并且可以相互竞争。 这比整个事情更令人愉快。
-
testing让你玩代码。 你可以做重构,你可以提高性能等等。而且,即使你不这样做,也很好意识到你有可能做到这一点。 一切都很好。
如何让自己做TDD? 你不需要! 如果你想获得所有列出的好处,那么你不会让自己 – 你只会快乐地做你的自由意志TDD。 如果你不需要/想要/能够获得他们 – 现在不要这样做。
TDD就是知道你在做什么和你做了什么。 如果我有一个正确的TDD设置,那么我可以注释掉一些可能不需要的代码行,然后立即运行testing,并确定testing是否真的需要破解 – 或者不是testing中断。 那些破解testing应该包含足够的描述,我可以理解为什么需要这行代码。
如果没有TDD识别在更改代码时出现什么问题,则需要进行可能的大规模人类回归testing,然后才能确定未破坏某些内容 TDD与全面质量pipe理(Total Quality Management)所有部分的目标是消除产品线末端大部分(如果不是全部)昂贵的手动testing需求。 怎么样? 知道你在做什么 怎么样? 通过测量你在做什么,并研究如何改善它。
如果您在代码中发现错误并修复它。 问题应该是:这是怎么发生的? 我可以防止这种情况发生吗? 怎么样?
这可能是要求不佳的。 这可能归结于您无法控制的因素。 关键是不要只是在地毯下刷问题,而是试着理解和提出如何做到这一点的知识,并将其纳入你的过程。
TDD只是其中的一部分,如果没有兼容和令人鼓舞的环境,将无法正常工作。
为了获得这种pipe理,需要纳入敏捷方法长期变得更便宜的基本原则。 敏捷方法的变化成本呈对数上升趋势,而没有敏捷方法的开发则使得变化成本随着时间呈指数增长。
有更多的阅读这个。 谷歌下面的“威廉·爱德华·戴明”,“全面质量pipe理”,“变化成本曲线”。
我的团队正试图更好地遵循TDD的方法 – 我不得不说,对于一个更大的开发团队来说,这不是一件容易的事情。
在编写任何生产代码之前,团队中的每个人都需要考虑他们的testing优先。 这可能是困难的,因为尽pipe人们可能会说他们会这样做,但是一旦他们到达键盘就不能保证。
对于一些pipe理人员来说,TDD似乎是一种昂贵的方法,因为我相信编写代码可能需要稍微长一点的时间(而这正是bean所反映的),但对于长期收益而言,这是短期的“痛苦”。 然后,你有(希望)写得很好,经过validation的代码,当事情发生变化时,有一个机制来处理将来的错误。
对于不太信服的人来说,通过例子给TDD一个阅读,然后尝试一下你的软件的迭代。
很多编码实践需要练习。
-
你可以在几个小时内学习和理解面向对象编程的概念。 但是很多程序员会承认,只是在实施OO编程大约2年之后才突然觉得“stream利”。
-
C ++程序员可以在几天内轻松“学习C#”。 但是,在他们“stream利”并理解他们正在编写的所有东西(而不是将C语言转换为C#的C ++程序员)的全部含义之前,它仍然需要大量的使用。
-
我最初学会了input“两指”的方式。 这个进步到了我可以用这种方法很快打字的地步,但我决定尝试学习触摸打字。 几个星期来,我强迫自己度过了使用触摸打字慢慢input所有内容的痛苦和沮丧。 这很难,需要真正的承诺,因为我input的每一行我只是想用我已经知道的方式打出来,这样可以给我快速的结果。 但最终我的触摸打字速度与我的双手打字相匹配。
-
当我第一次使用MFC时,我抵制了MFC前缀约定(例如“m_”和“p”前缀)。 它看起来丑陋,荒谬和无意义的额外打字。 然后我强迫自己使用这个系统一个月,过了这段时间,我不明白自己是如何编写代码而没有这个命名约定的 – 它帮助我更好,更快地理解代码,并减less了愚蠢的错误数量。
那么你如何发展TDD的学科呢? 只需承诺自己做TDD一个月。 这将是缓慢的。 这将是艰难的。 只是甩掉几条“老路”而已。 但过了一段时间,你将积累经验和速度,这样做会变得更容易和更自然。 然后,您可以评估TDD和非TDD,并确定哪种方法最好。 你可能不会回头。
如果你处于真空状态,TDD是非常难以开始的,而当你的团队中的其他人仅仅对TDD提供口头的服务时,几乎不可能做到这一点。 我的build议是找一个地方(无论是在工作中还是在工作之外),在那里你可以自己编程,或者更好地与一个忠诚的朋友或同事一起,并且始终如一地开始这样做 – 毫无例外。
TDD(和敏捷方面)只有在你和你的团队全力支持时才开始点击,并致力于使其工作。 你需要在你的腰带上获得一些TDD的胜利,这将会使你成为一个信徒。 那么回到你的其他项目,并成为一个冠军。 开始某个地方,你可以得到那些关键的早期胜利。 它使所有的差异。
你的TDD真言当天…
TDD中的T是TODO或任务驱动的
我更愿意鼓励人们考虑TDD是要完成任务(即testing或规格)。 (任务不完整为红色,任务完成为绿色。)
有一个连续的testing运行器,显示您的testing/规格的结果是获得testing习惯的主要帮助。 就我个人而言,我认为这是一个重要的反馈循环,使TDD立即感到生产力/鼓励。
当然,当你开始重构和改变代码库的时候,你也可以得到关于回归错误的即时反馈,这也是所有好处的一部分。
国际海事组织,TDD与testing/代码/重构 – 听起来不错,有趣,但:
- 没有硬性规定必须按照这个顺序来完成
- 很多时候,我发现自己编写代码,然后写testing和发现我错过的有趣掘金
- 其他时候,我去testing>代码>重构周期,并享受这一切。
- 我认为重要的是要有unit testing的代码和足够的覆盖范围来确保所有重要的path都被覆盖。 达到这个目标的顺序并不那么重要。
HTH。
编辑:进一步澄清我的观点
- 虽然我更喜欢从编写testing开始,但是有时通过编写代码(特别是对于新function和/或新项目)来更好地理解格局,然后编写testing
- 但是,如果我正在重构代码和/或修复错误,我将首先检查是否写了testing,并且足以涵盖代码的function。 如果没有,首先我会写testing然后做重构。