TDD的红色-绿色-重构周期已经建立并被接受。我们编写一个失败的单元测试,并使其尽可能简单地通过。与为一个类编写许多失败的单元测试并使它们全部一次性通过相比,这种方法有什么好处。
测试套件仍可保护您避免在重构阶段编写错误的代码或犯错误,那么这有什么危害呢?有时,首先以“大脑转储”的形式首先编写一个类(或模块)的所有测试会更容易,以便一次编写所有预期的行为。
TDD的红色-绿色-重构周期已经建立并被接受。我们编写一个失败的单元测试,并使其尽可能简单地通过。与为一个类编写许多失败的单元测试并使它们全部一次性通过相比,这种方法有什么好处。
测试套件仍可保护您避免在重构阶段编写错误的代码或犯错误,那么这有什么危害呢?有时,首先以“大脑转储”的形式首先编写一个类(或模块)的所有测试会更容易,以便一次编写所有预期的行为。
Answers:
测试驱动的设计是关于正确使用API而不是代码。
首先编写最简单的失败测试的好处是,使您的API(实际上是您在动态设计中)尽可能地简单。在前面。
任何将来的用途(您编写的下一个测试所用的内容)都将来自最初的简单设计,而不是次要的设计来应对更复杂的情况。
当您编写一个测试时,您会专注于一件事。
通过许多测试,您可以将注意力转移到许多任务上,所以这不是一个好主意。
编写单元测试时的困难之一是您正在编写代码,而代码本身可能容易出错。在编写实现代码时,由于重构工作,最终还可能需要稍后更改测试。使用TDD,这意味着您可能最终会在测试中有些失落,并发现自己需要在项目过程中随着实施的成熟而重写大量“未经测试”的测试代码。避免此类问题的一种方法是简单地专注于一次做一件事情。这样可以确保将所有更改对测试的影响最小化。
当然,这很大程度上取决于您编写测试代码的方式。您是针对每种方法编写单元测试,还是针对功能/需求/行为编写测试?另一种方法可能是将“行为驱动”方法与合适的框架一起使用,并专注于编写测试,就像它们是规范一样。这意味着要么采用BDD方法,要么采用BDD测试(如果您希望更正式地坚持使用TDD)。另外,您可能会完全遵循TDD范式,但是会更改编写测试的方式,这样,您就不必再完全专注于测试方法,而可以更一般地测试行为,从而满足所实现需求特征的特定要求。
无论您采用哪种具体方法,在上述所有情况下,您都使用测试优先方法,因此,虽然可能很容易将大脑下载到一个漂亮的测试套件中,但您也想与诱惑要做的比绝对必要的多。每当我要开始一个新的测试套件时,我都会对自己重复YAGNI,有时甚至将其放入代码中的注释中,以提醒我继续专注于紧迫的事情,并只做满足要求的最低要求。我即将实现的功能要求。坚持红绿重构有助于确保您将执行此操作。
我认为通过这样做,您会错过TDD 的过程。通过仅在开始时编写所有测试,您实际上并没有完成使用TDD进行开发的过程。您只是在猜测您将需要哪些测试。如果您在开发代码时一次进行一次测试,则这将与最终编写的测试完全不同。(除非您的程序本质上是微不足道的。)
我确实在“脑力激荡”时“编写”了所有我能想到的测试,但是我将每个测试写成描述该测试的一个注释。
然后,我将一个测试转换为代码并进行工作,以使其编译并通过。通常,我认为我并不需要我认为曾经做过的所有测试,或者我需要其他测试,这些信息仅来自编写代码以使测试通过。
问题在于,除非创建了要测试的方法和类,否则您无法在代码中编写测试,否则您将得到很多编译器错误,这些错误会导致您一次进行单个测试。
现在,如果使用“英语”编写测试时使用的是规格流程之类的系统,则您可能希望让客户在有空的时候同意一系列测试,而不是仅创建一个测试。
您假设您在编写代码之前知道代码的外观。TDD / BDD和QA一样,既是设计/发现过程。对于给定的功能,您可以编写最简单的测试来验证该功能是否令人满意(由于功能的复杂性,有时可能需要多次测试)。您编写的第一个测试加载了对工作代码的外观的假设。如果在编写第一行代码以支持该测试套件之前就编写了整个测试套件,则可能会产生一系列未经验证的假设。相反,编写一个假设并进行验证。然后写下。在验证下一个假设的过程中,您可能只是打破了先前的假设,因此您必须返回并更改该第一个假设以匹配现实,或者更改现实以使第一个假设仍然适用。
考虑一下您在科学笔记本中编写的作为理论的每个单元测试。填写笔记本时,您可以证明自己的理论并形成新的理论。有时证明一个新理论会反驳一个先前的理论,因此您必须加以修正。一次证明一种理论比一次证明20种理论容易。
TDD是一种高度迭代的方法,根据我的经验,它更适合现实世界的开发方式。通常,我的实施过程是在此过程中逐步形成的,每个步骤都可能带来更多的问题,见解和测试思路。这是使我专注于实际任务的理想选择,并且非常高效,因为在任何时间点,我只需要在短期记忆中保留有限数量的内容。反过来,这减少了出错的可能性。
您的想法基本上是一种“前期大测试”方法,恕我直言,这种方法更难处理,并且可能变得更加浪费。如果您在工作中发现自己的方法不好,API有缺陷并且需要从头开始,或者改用第三方库,该怎么办?这样一来,预先编写测试的大量工作就浪费了精力。
就是说,如果这对您有用,那就好。我可以想象,如果您按照固定,详细的技术规范进行工作,在您经验丰富的领域和/或完成的工作量很小,那么您可能已经准备了大多数或所有必要的测试用例,并且可以清楚地实现开始。然后可能需要立即编写所有测试来开始。如果您的经验是,从长远来看,这会使您的工作效率更高,那么您不必太担心规则手册:-)
除了想一件事,TDD的一种范例是编写尽可能少的代码以通过测试。一次编写一个测试时,看到编写足以使该测试通过的代码的路径要容易得多。要通过一整套测试,您无需花很多时间就可以编写代码,而必须做出巨大的飞跃才能一次性通过所有测试。
现在,如果您不局限于编写代码以使它们全部“一次通过”,而是只编写足够多的代码来一次通过一个测试,则它可能仍然有效。但是,您必须有更多的纪律,不仅可以继续编写和编写超出所需数量的代码。一旦走上了这条路,您就可以编写超出测试描述范围的代码,而这些代码是未经测试的,至少在某种意义上说它不是由测试驱动的,或者在不需要的情况下(或行使)任何测试。
完全理解该方法应该做什么,例如注释,故事,功能说明等。我将等待一次将它们转换为测试。
一次编写所有测试可能会错过的另一件事是思考过程,通过该过程可以促使您想到其他测试用例。如果没有大量现有测试,则需要在上一个通过测试的上下文中考虑下一个测试用例。就像我说的那样,对方法应该做什么有一个很好的了解,但是很多时候我发现自己发现了一些新的可能性,而这些新的可能性我不是先验的,而只是在编写过程中才会发生。测试。除非您特别习惯于思考我可以编写哪些我还没有编写的新测试,否则您可能会错过这些测试。
我从事的项目中,编写(失败)测试的开发人员与实施必要代码以使其通过的开发人员不同,我发现它确实有效。
在那种情况下,仅与当前迭代相关的测试只编写一次。因此,您的建议在这种情况下是完全可能的。
Red-Green-Refactor循环是一个清单,供刚接触TDD的开发人员使用。我想说,遵循此清单是一个好主意,直到您知道何时遵循以及何时可以打破它为止(也就是说,直到不必在stackoverflow上问这个问题为止:)
完成TDD已有近十年的时间,我可以告诉您,在编写生产代码之前,很少(如果有的话)编写很多失败的测试。
一次测试:主要优势是专注于一件事。想一想深度优先的设计:您可以深入了解并通过快速反馈环保持专注。您可能会错过整个问题的范围!这就是(大型)重构起作用的时刻。没有它,TDD将无法正常工作。
所有测试:分析和设计可能会向您显示更多问题的范围。想想宽度优先的设计。您可以从多个角度分析问题,并从经验中补充意见。它本来就更难,但是如果“足够多”,可能会产生有趣的好处-减少重构。请注意,过度分析很容易,但完全没有达到目标!
我发现通常很难推荐偏爱其中一种,因为因素很多:经验(尤其是同一个问题),领域知识和技能,重构代码的友好性,问题的复杂性...
我猜想,如果我们将注意力集中在典型的业务应用程序上,那么TDD及其快速,几乎试错的方法通常会在有效性方面取胜。
假设您的测试框架支持它,那么我建议您不要实施您想转储的测试,而是编写描述性的待决测试,稍后再实现。例如,如果您的API应该执行foo和bar而不能执行biz,则只需为测试套件添加以下代码(此示例在rspec中),然后逐一攻击即可。您很快就会下定决心,可以一一解决所有问题。当所有测试通过时,您将知道何时解决了您在头脑风暴期间遇到的所有问题。
describe "Your API" do
it "should foo" do
pending "braindump from 4/2"
end
it "should bar" do
pending "braindump from 4/2"
end
it "should not biz" do
pending "braindump from 4/2"
end
end