【问题标题】:How do I implement a test framework in a legacy project如何在遗留项目中实现测试框架
【发布时间】:2010-12-21 16:29:43
【问题描述】:

我有一个用 PHP 和 Javascript 编写的大项目。问题是它变得如此庞大且不可维护,以至于更改代码的一小部分会扰乱并可能破坏很多其他部分。

我真的不擅长测试自己的代码(事实上,其他人每天都会指出这一点),这使得维护项目变得更加困难。

项目本身并没有那么复杂或复杂,更复杂的是它的构建方式:我们在进行测试时没有预定义的规则或列表要遵循。这通常会导致大量错误和不满意的客户。

我们开始在办公室讨论这个问题,并提出了开始使用测试驱动开发而不是像地狱一样的开发并可能稍后进行测试的想法(这几乎总是最终总是修复错误)。

在那个背景之后,我需要帮助的事情如下:

  1. 如何实施测试 框架到一个已经存在的 项目? (在 3 年 制作和计数)

  2. 有哪些框架 供测试用?我想我需要一个 Javascript框架和 一个用于 PHP。

  3. 什么是最好的测试方法 图形用户界面?

我以前从未使用过单元测试,所以这对我来说真的是未知领域。

【问题讨论】:

标签: php javascript frameworks testing


【解决方案1】:

生日,

编辑:我刚刚快速浏览了“The Art of Unit Testing”的第一章,它也可以在book's website 上以a free PDF 的形式获得。它可以让您很好地了解您尝试对单元测试执行的操作。

我假设您将使用 xUnit 类型的框架。一些初步的高级想法是:

  1. 编辑: 确保每个人都同意什么是好的单元测试。我建议使用上面的概述章节作为一个很好的起点,如果需要,可以从那里开始。想象一下,当人们对什么是“好”的单元测试有不同的理解时,他们热情地跑去创建大量的单元测试。如果你将来发现 25% 的单元测试没有用、没有可重复性、不可靠等等,对你来说会很糟糕。
  2. 添加测试以一次覆盖小块代码。也就是说,不要创建一个单一的任务来为现有代码库添加测试。
  3. 修改任何现有流程以确保为编写的任何新代码添加新测试。将必须为新功能提供单元测试的代码审核过程作为一部分。
  4. 扩展任何现有的错误修复流程,以确保创建新测试以显示存在并证明错误不存在。注:不要忘记回滚您的候选修复以再次引入错误,以验证只有一个补丁解决了问题,而不是由多种因素组合修复。
  5. 编辑:当您开始增加测试数量时,开始将它们作为夜间回归测试运行,以检查新功能是否破坏了任何内容。
  6. 成功运行所有现有测试和候选错误修复审查过程的准入标准。
  7. 编辑: 开始保留测试类型的目录,即测试代码片段,以便更轻松地创建新测试。一直在重新发明轮子是没有意义的。为测试在代码库的一部分中打开文件而编写的单元测试将类似于为在代码库的不同部分中打开不同文件的测试代码而编写的单元测试. 将它们编入目录以便于查找。
  8. 编辑:如果您只修改现有类的几个方法,请创建一个测试套件来保存该类的完整测试集。然后仅将您正在修改的方法的单个测试添加到此测试套件中。这使用 xUnit 术语,因为我现在假设您将使用像 PHPUnit 这样的 xUnit 框架。
  9. 使用标准约定命名您的测试套件和测试,例如testSuite_classA 然后将包含单独的测试,如 test__test_function。例如,test_fopen_bad_name 和 test_fopen_bad_perms 等。这有助于最大限度地减少在代码库中移动和查看其他人的测试时的噪音。它还可以帮助人们首先为他们的测试命名,让他们解放思想,专注于更有趣的事情,比如测试本身。
  10. 编辑:在这个阶段我不会使用 TDD。根据定义,TDD 将需要在更改到位之前存在所有测试,因此当您添加新的 testSuite 以覆盖您正在处理的类时,您将到处都有失败的测试。而是添加新的 testSuite,然后根据需要添加单独的测试,这样您的测试结果中就不会因为失败的测试而出现很多噪音。而且,正如 Yishai 指出的那样,在这个时间点添加学习 TDD 的任务真的会减慢你的速度。把学习 TDD 作为你有空闲时间完成的任务。没那么难。
  11. 因此,您需要一个工具来跟踪那些存在 testSuite 但尚未编写测试以覆盖类中其他成员函数的现有类。通过这种方式,您可以跟踪测试覆盖范围的漏洞。我在这里谈论的是高级别的,您可以在其中生成当前不存在测试的类和特定成员函数的列表。测试和 testSuites 的标准命名约定将极大地帮助您。

我会根据我的想法添加更多积分。

HTH

【讨论】:

  • 我应该立即为完整的类编写测试用例,还是只为方法编写测试用例?请记住,我是这个单元测试业务的新手 :)
  • @Skoog,刚刚添加了一个指向单元测试优秀介绍的链接。
  • 我代表我们整个团队感谢您。你提供了很多好东西。
【解决方案2】:

你应该给自己一份Working Effectively with Legacy Code。这将为您提供很好的指导,帮助您将测试引入到不是为测试而编写的代码中。

TDD 很棒,但您确实需要从测试现有代码开始,以确保您所做的更改不会在引入更改时更改现有的必需行为。

但是,现在引入 TDD 会让您在重新开始之前慢很多,因为改造测试,即使只是在您正在更改的区域中,也会在变得简单之前变得复杂。

【讨论】:

  • @Yishai,关于延迟引入 TDD 的潜在影响的好点。我认为它可以添加到全新的代码中,尽管如果人们已经知道 TDD。同时学习 TDD、xUnit 框架和单元测试基础知识不会效果很好!
【解决方案3】:

只是为了补充其他出色的答案,我同意一次性将覆盖率从 0% 提高到 100% 是不现实的 - 但您绝对应该在每次修复错误时添加单元测试强>。

您说有很多错误和不满意的客户 - 我非常赞成将严格的 TDD 纳入错误修复过程,这比整体实施要容易得多。毕竟,如果确实存在需要修复的错误,那么创建一个重现它的测试可以达到各种目标:

  • 这可能是证明确实存在问题的最小测试用例
  • 确信(当前失败的)测试会突出报告的问题,您将确定您的更改是否已修复它
  • 它将永远作为一种回归测试,以防止将来再次出现同样的问题。

在现有项目中引入测试很困难,而且可能是一个漫长的过程,但在修复错误的同时进行测试是这样做的理想时机(与“正常”意义上的逐步引入测试类似)如果不抓住这个机会从你的错误报告中制作柠檬水,那将是一种耻辱。 :-)

【讨论】:

  • 我已经接受了这是一个漫长的过程。所以我准备好了。我最关心的是,我从哪里开始?我该如何进行?但是你提到的事情,我理解。我认为这对我们非常有用。我只需要阅读 TTD 和单元测试的主题。您会碰巧有一些适合该领域初学者的好资源吗?
【解决方案4】:

从规划的角度来看,我认为你有三个基本的选择:

  1. 花一个周期用单元测试来改进代码
  2. 指定团队的一部分通过单元测试改进代码
  3. 在编写代码时逐渐引入单元测试

第一种方法的持续时间可能比您预期的要长得多,并且您的可见生产力会受到打击。如果您使用它,您将需要获得所有利益相关者的支持。但是,您可以使用它来启动该过程。

第二种方法的问题在于您在编码人员和测试编写人员之间进行了区分。编码人员不会对测试维护有任何所有权。我认为这种方法值得避免。

第三种方法是最有机的,它让您从一开始就进入测试驱动开发。积累有用的单元测试体可能需要一些时间。缓慢的测试积累速度实际上可能是一个优势,因为它让您有时间擅长编写测试。

考虑到所有因素,我认为我会选择本着方法 1 的精神进行适度的冲刺,然后致力于方法 3。

关于单元测试的一般原则,我推荐 Gerard Meszaros 的书 xUnit Test Patterns: Refactoring Test Code

【讨论】:

  • 我认为这将是我的后端代码最有可能的方法。我不知道我们是否能够将整个 sprint 用于改造。但我敢肯定,当我们处理每个类时,我们可以随时为每个类编写测试用例。
【解决方案5】:

我使用PHPUnit 效果很好。 PHPUnit 与其他 JUnit 派生项目一样,要求将要测试的代码组织到类中。如果您的项目不是面向对象的,那么您需要开始将非过程代码重构为函数,并将函数重构为类。

我个人没有使用过 JavaScript 框架,但我认为这些框架还需要将您的代码结构化为(至少)可调用函数(如果不是完整的对象)。

对于测试 GUI 应用程序,您可能会从使用 Selenium 中受益,尽管由具有良好 QA 直觉的程序员编写的检查表可能工作得很好。我发现使用 MediaWiki 或您最喜欢的 Wiki 引擎是存储清单和相关项目文档的好地方。

【讨论】:

  • 碰巧 70-80% 是基于类的,其余的仍然是程序性的。仍在努力让剩下的 20% 也能上课。
  • +1 PHPUnit。我听说过关于 Selenium 的混合报道,因为它倾向于鼓励脆性测试。不过,我无法推荐一个更 xUnit-y JS 测试框架。如果 JS 是生成的,GWT 风格,那么就有一个 xUnit。
【解决方案6】:

在大多数情况下,实施框架是一项复杂的任务,因为您开始使用一些新的可靠框架部分来重建旧代码。那些旧部分必须开始与框架进行通信。旧部分必须接收一些回调和返回状态,然后旧部分必须以某种方式向用户指出这一点,实际上您突然有 2 个系统要测试。

如果您说您的应用程序本身并不复杂,但由于缺乏测试而变得复杂,那么重建应用程序可能是一个更好的选择。将一些常见的框架(如 Zend)进行测试,收集您的需求,确定测试的框架是否适合需求,并确定重新开始是否有用。

我不太确定单元测试,但 NetBeans 有一个内置的单元测试套件。

【讨论】:

    【解决方案7】:

    如果代码真的很乱,可能很难进行任何单元测试。只有足够松散耦合和设计良好的组件才能轻松进行单元测试。但是,在您的情况下,功能测试可能更容易实现。我建议看看Selenium。使用这个框架,您将能够同时测试您的 GUI 和后端。但是,它很可能无法像单元测试那样帮助您捕捉错误。

    【讨论】:

    • 我会说大约一半的错误在 GUI 本身,所以 Selenium 在这里可能是一个非常好的选择。我会检查是否出来。谢谢。
    【解决方案8】:

    也许这份清单会帮助你和你的伙伴重新组织一切:

    1. 使用 UML 来设计和处理异常 (http://en.wikipedia.org/wiki/Unified_Modeling_Language)
    2. 使用 BPMS 来设计您的工作流程,让您轻松自如 (http://en.wikipedia.org/wiki/Business_process_management)
    3. 获取还支持 javascript 后端的 php 框架列表(例如 Zend with jQuery)
    4. 比较这些框架并选择最符合您的项目设计和之前使用的编码结构的框架
    5. 您应该考虑使用 ezComponents 和 Dtrace 等工具进行调试和测试
    6. 不要害怕变化 ;)

    【讨论】:

      【解决方案9】:

      对于 GUI 测试,您可能想看看 Selenium(正如 Ignas R 已经指出的那样)或者您也可能想看看这个工具:STIQ

      祝你好运!

      【讨论】:

        【解决方案10】:

        在某些情况下,进行自动测试可能不是一个好主意,尤其是当代码库很脏并且 PHP 将其行为与 Javascript 混合时。

        最好从一个简单的清单开始(带有链接,以使其更快),在每次交付之前应该(手动)对事物进行测试。

        在 3 年前的雷区编码时,通过许多错误检查更好地保护自己。为每个案例编写正确的错误消息所花费的 15 分钟不会丢失。

        使用桥接方法:通过调用 fnew() 来桥接丑陋冗长的函数 fold(),fnew() 是一些干净类的包装器,调用 fold 和 fnew 并比较结果,记录差异,将代码投入生产并等待为你的鱼。执行此操作时,始终使用一个周期进行重构,另一个周期用于更改结果(甚至不修复旧行为中的错误,只需桥接它)。

        【讨论】:

          【解决方案11】:

          我同意 KOHb,硒是必须的!

          也可以看看PHPure

          他们的软件会记录工作 php 网站的输入和输出,然后自动为不访问外部源(db、文件等)的函数编写 phpunit 测试。

          这不是一个 100% 的解决方案,但它是一个很好的开始

          【讨论】:

            猜你喜欢
            • 2016-02-21
            • 1970-01-01
            • 2020-06-17
            • 2011-03-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-01-16
            • 1970-01-01
            相关资源
            最近更新 更多