用单元测试重写PHP代码一个项目的生命周期中有两个时刻,在那里您可以应用一个漫长的知名的最佳实践:«首先写测试,然后是代码»。第一个是当你开始编写代码时,你有一段时间。第二个是当软件的当前架构达到其最大潜力时,它是重写它的时间,或者至少是其主要组件之一。那’■当您独自留在黑暗中,使用白色页面和一堆单位测试:唯一的解决方案是用单元测试重写PHP代码。

这是我自己的故事。

从头重写PHP代码是一个坏主意

从头开始重写是工具胸部最糟糕的想法:这意味着摧毁建设前一个版本的同时积累的所有经验,可能会再次做同样的错误。而且,在你’在那样,世界其他地方都不会等待,但耐心地堆积了您的软件的新挑战。大学教师’在没有完全疯狂或充分理由的情况下走下那条路。

并且由于原因很好,重建核心就是其中之一。它必须在意识到你可以’t克服了当前的代码’使用小补丁的限制:它必须是一个重大升级,一个将留出几个初始概念。因此,作为重写的第一步,您需要从前一个版本中学习在第二个版本中避免的内容。这样,它不是完全重写,虽然你’ll从白页开始。

测试是硬编码的经验

另一个没有丢失的经验来源是这样的努力是单位测试。如果测试通过,新版本可以使用以前版本中的所有可能。因此,保持那些旧的测试可以作为构建新代码的指导。我们’LL看到100%代码覆盖是可取的,但没有必要。

那’S我与exakat引擎从Gremlin 2.0到Gremlin 3.0的举动有关。 Gremlin是一个开源图遍历语言,即平台形式不可知论。当它是Neo4j的默认部分时,我开始使用它,围绕1.7版,并在现在继续跟踪它。几年后,Neo4J现在正在推动自己的蓄电池语言,使得更难使用Gremlin,而且,Gremlin 2.0达到了生命结束。

Gremlin 2.0至3.0的迁移比预期更困难。取而代之的六百个查询,被证明是困难的:显然,我使用了很多没有更多的特殊技巧。特别是,我对封闭和λ函数进行了沉重的使用,基于Groovy,Gresovy,Groovy在Gremlin中使用的脚本语言。这仍然是Gremlin 3.0的一部分,但它是隐藏的,Gremlin正在迁移到更抽象的语言水平:更多声明性感谢命令。

几次尝试升级第一个查询后,很快就会意识到是内部发动机重写的时间,我决定重写exakat的核心部分。我完全了解要更改的内容(将更多逻辑移动到PHP中,减少导入,使单遍PHP代码分析仪进行一次,并让我让我免于犯错误。

第一天:1786年测试,没有代码

第一天致力于收集测试,并为新代码准备项目。当然,我检查了我的UT框架,因为没有代码,我有1784个错误。就是那个’如果我意识到2个测试通过,即使没有实际代码。所以,我审查了它们:那些正在检查一些不良行为没有发生,但它没有’t检查同时强制执行的好行为。想象一下,测试是确保没有‘Z’出现在结果中,但没有’检查结果是......非空的。测试仍然有用,但无论如何,我升级了它们。

然后,我决定选择其中一个ut并使这是我的第一个目标。这给了我一个新的代码的目标,并在UT报告上进行第一点。显然,我稍后离开了硬测试,然后选择一个简单的测试(整数添加):然后我开始编码。已经对概念进行了很容易。很快,我意识到我也需要一种从旧UT调用新对象的方法,并且必须编写一些适配器。而且,一种阅读新代码的新结果的方法,并使旧的UT也会理解它。在为每个绕道弯曲的方式走开后,我在ut上得到了我的第一个点!虽然,太三天了!

是的,三天。实际的核心编码非常迅速,但所有的辅助任务,如命令行解析,记录,调试和适应ut,也可以在第一步完成。所以,看起来我正在做很多编码,只是为了检查旧代码的一个非常简单的功能。 3天真的很长,最后,压力很大:我真的会在3 * 1785天内完成这份工作吗?

第二单元测试通过

第二个通过«添加变量»。现在,这个又一天的半天。与之前的3天相比,这很快,而且它也非常平坦。我现在已经习惯了新的代码,我可以参考已经工作的代码以获取帮助。我仍然必须建立一些缺少的功能(早期阶段,对),但我也有一些工作,所以它更快。

第三和第四个也是如此。接下来的30 ut速度更快,更快,所以我以一种良好的节奏完成了第一周和一些恢复的热情。除此之外,新的代码给出了一些很好的速度和稳健性的迹象,但虽然UT MOSEN’根本展示。实际上,每周2%的UT不是预期的结果。

快速且令人满意的阶段

第二周开始强劲,旧的ut从f移动得非常快,稳定,直到我达到1400 ut的水平。与第一周的30 ut相比,这更好。进展不顺畅:有时,添加一个新功能将使100 UT工作同时工作,然后调整2小时的相同功能只会使2个额外的UT通行证:没有真正的受控进展感。显然,一些冗余的测试,有些测试只是另一个人的变化。此外,一些测试是为了确保先前实施的边缘情况是可以的,而且它们只是不打击’通过新的实现,不再需要。那很酷。

当然,在另一方面,如果版本1的边缘案例快速传递,它也意味着版本2的边缘情况不同,新的和其他地方但尚未知道。这是第三周的主要痛苦。第三周看到新的测试通过的增加,以及回归的外观。实际上,这可能是前一阶段要快速的另一个原因:每个特征是独立于其他功能的准备。它们也被独立测试(例如,没有乘法的测试添加)。当独立测试运行时,我开始运行运行的测试,同时处理两个(或更多)功能。并且,一旦我找到了两个功能将如何解决优先权,那么通常意味着代码的其他部分是在废墟中。回归是一个均值的球员。

这是回归的

因此,在第三周,通过的演变远远下来。一般趋势仍在上涨,达到100%,但也有峰值和山谷。峰倾向于较小且较小,山谷较少,深层。它们也在彼此进一步增长,因此每个新的回归对所述其他代码的影响较少。

最后100%UT

到第三周结束时,新代码通过所有旧的UT。新的代码保持其潜力和速度改善,因此整个重写非常值得。可悲的是,该软件的第二部分并不乐于与新的第一部分合作:现在的一些人现在失败了。

所以必须做出一些结论:

  • 在重建核心部分时,需要制作更多UT,因为现在已经弃用了旧的一部分,并且将出现新的问题和边缘案例。我想我赢了’T降低旧测试,但创造了新的测试,大概约为10-15%。
  • 有‘leaks’在软件的两个内部部分之间,这意味着第一部分的一些结果实际上在第二部分中进行了测试。第一阶段中的UT主要检查了所有运行正常,而且没有任何阻挡分析。但实际的价值测试由第二阶段完成。这意味着另外1400 UT检查。
  • 在牧养新版本中做得很好:有目标要专注于微目标(让’制作该测试通行证)和全球目标(让’S制作所有测试通过)并具有进展的规模(80%UT通过,是的!)。另一方面,UT没有’T帮助组织工作时间表。
  • ut作为目标是不够的:一些测试是错误的,一些测试丢失了(100%覆盖率,任何人?),有些对旧版本有利,但为新版本没用。到底,拥有它们真的很好。更重要的是,现在需要一些新的测试,因为新的实施失败了旧的一个人没有’t and vice-versa.
重写它时随着时间的推移的%:不是简单的线条
重写它时随着时间的推移的%:不是简单的线条

大学教师’t忘记了手动测试

作为最终说明,这里是我在此期间使用的技巧:记录所有手动测试。作为初始阶段的一部分,我意识到我要做很多手动测试:我从UT开始,但是,只要确定,我也会改变输入以检查某些边缘案例是否覆盖。这些手动测试对于DEV非常重要,并且应稍后重复使用。因此,作为UT Framework的额外层,我添加了一个保持所有手动测试的记录器。一世’ve收集了另一个2820的测试:即使它们中的许多已经制成了UT,那将是一个添加新ut的宝箱。