单元测试的整体情况



这不是有关需要在代码编辑器中输入哪些字符以获得单元测试的指南。 这是心灵的食物, 采取这些行动之前必须先食用它们。

单元测试的主题并不像看起来那么简单。 我们许多开发人员在客户,员工,同事,他们的偶像等人的压力下进行单元测试。 我们很快了解了它的价值,并且在完成了技术准备后,甚至根本不了解它,就忘记了总体情况。 在本文中,我将简要讨论一般和PHP中什么是单元测试,什么不是,同时,我将描述QA领域中单元测试的位置。

什么是测试?


在研究单元测试之前,您需要学习测试本身的理论,以免像最流行的PHP框架之一的作者所犯的那样犯错误:他们在其网站上展示了集成测试,并将其称为单元测试。 不,Laravel,这些不是单元测试。 尽管这并不能阻止我仍然喜欢这个框架。

软件测试的定义是“进行调查以向有关方面提供有关产品质量的信息。” 这与“软件测试浪费了开发人员的预算,他们没有做任何重要的事情,然后要求更多的时间和金钱,因为”没有什么”可能会非常昂贵。 这里没有新内容。

这是我成为测试的简短历史:

  • 1822年-差速器(Charles Babbage)。
  • 1843年-分析引擎(Ada Lovelace)。
  • 1878年-爱迪生(Edison)引入了“ bug”一词。
  • 1957年-程序的测试和调试(Charles Baker)。
  • 1958年-第一个软件测试小组(Gerald Weinberg)。
  • 1968年-危机组织PO(Friedrich Bauer)。
  • 1970年代-瀑布模型,关系模型,分解,关键分析( 演练 ),代码的设计和检查,质量和指标,设计模式。
  • 1980年代-CRUD分析,系统架构,自动测试,V模型,可靠性,质量成本,使用方法,OOP设计模式。
  • 1990年代-Scrum,可用性测试,MoSCoW,启发式测试,软件自动化和测试。

如果您与像我这样的千禧一代有关系,您可能会惊讶于测试团队在您出生之前就已经存在很长时间了。 停一会儿,吸气,呼气,冷静下来。
历史记录显示了对相关方“足够好”的测试类型如何随时间变化。 测试期间指导的大概阶段:

  • ...-1956年调试
  • 1957年-1978年示威
  • 1979年-1982年遭到破坏
  • 1983年-1987年估计
  • 1988年-...预防

因此,必须进行单元测试以防止项目和实施之间的差异。

真正的测试是什么?


软件测试有不同的分类。 为了更好地理解单元测试的位置,我仅提及最广泛的方法。

测试包括:静态和动态,“框”(白框,黑框,灰框),级别和类型。 每种方法使用不同的分类标准。

静态和动态测试


静态测试无需执行代码即可执行。 这包括校对,验证,代码修订(在观察另一个/结对编程工作时),关键分析,检查等等。

动态测试以获得正确的结果需要执行代码。 例如,用于单元测试 ,集成,系统,验收和其他测试。 即,使用动态数据,输入和输出来执行测试。

盒式方法


根据这种方法,所有软件测试都分为三种类型的框:

  • 白盒测试将验证内部结构和模块,忽略最终用户的预期功能。 这可以是API测试,故障注入, 单元测试 ,集成测试。
  • 黑匣子测试软件的功能更感兴趣,而不是软件的功能。 这意味着测试人员无需了解测试对象或了解其在内部的工作方式。 这种类型的测试针对最终用户,即他们与可见界面进行交互的经验。 黑匣子包括基于模型的测试,使用情况测试,状态转换表,规范测试等。
  • 灰盒 ”类型的测试是在了解软件算法和数据结构的情况下设计的(白盒),但在用户级别(黑盒)执行。 这包括回归测试和模式测试。

现在,让您感到困惑的是, 单元测试也可以应用于“黑匣子”,因为您可以了解被测模块,但不能了解整个系统。 尽管对我来说,它仍然是一个“白盒子”,但我建议您同意这一点。

测试等级


它们的数量通常在4到6之间变化,并且它们都很有用。 名称也可以不同,具体取决于公司采用的文化,您可能将“集成”测试称为“功能”,将“系统”测试称为“自动化”,等等。 为简单起见,我将描述5个级别:

  1. 单元测试
  2. 集成测试。
  3. 测试组件接口。
  4. 系统测试。
  5. 操作验收测试。

单元测试测试特定代码段的功能,通常一次测试一个功能。 集成测试检查组件之间的接口,以使组装在一起的模块形成按预期工作的系统。 这一点很重要,因为大量的测试(称为单元测试)实际上是集成测试,开发人员将其视为模块。 如果您打算使用多个模块-这是在测试它们之间的集成,而不是模块本身。 测试组件接口检查在不同模块之间传输的数据。 例如,我们从模块1接收数据-已检查-传输至模块2-已检查。 系统测试是端到端测试,以验证是否符合所有要求。 执行操作验收测试以验证操作准备情况。 它不起作用,仅检查服务的可维护性,是否有子系统破坏环境和其他服务。

测试类型


每种测试类型,无论其级别如何,都可以分为其他类型。 有20多种常见类型。 最常见的:

  • 回归测试
  • 验收测试。
  • 烟雾测试
  • at
  • 破坏性测试
  • 性能测试。
  • 持续测试
  • 可用性测试。
  • 安全测试。

从名称中可以清楚看出为什么要进行这种或那种类型的测试。 粗体是PHP中的单元测试。 如果确实需要,则可以将这些术语中的每一个应用于单元测试。 但是,单元测试的主要种类是回归测试,它可以在更改代码后检查系统的所有模块是否正确执行。

现在您知道单元测试是动态的,属于“白盒”类,在模块级别执行,是回归测试,但是模块化测试可以理解为许多类型的测试。 那么什么是单元测试呢?

什么是单元测试?


V模型是上述级别,类型及其在软件开发生命周期中的用途的图形表示。



在检查并批准了产品的详细要求之后,当他们开始编写代码时,单元测试就成为抵御任何不一致之处的第一道防线。 因此,了解自己正在做什么的公司正迫使开发人员使用单元测试甚至TDD,因为在初始阶段修复错误要比在后期修复中便宜得多。

这是公平的。 单元测试有很多优点。 他们是:

  • 隔离程序的每个部分并检查其正确性。
  • 帮助及早发现问题。
  • 它们使开发人员根据输入,输出和错误条件进行思考。
  • 它们使代码看起来更方便测试,便于将来的重构。
  • 简化工作模块(!)的集成。
  • 部分替换技术文档。
  • 强制将接口与实现分开。
  • 他们证明模块代码按预期(至少在数学上)正常工作。
  • 可用作低级回归测试套件。
  • 演示不完整的系统集成方面的进展。
  • 降低修正错误的成本(使用TDD甚至更多)。
  • 它们使您可以通过确定模块的职责来改进应用程序的体系结构。
  • 如果可以测试,则可以连接到系统。
  • 单元测试很有趣!

但是,您可能会在阅读以下列表时考虑到某些限制:

  • 单元测试不会捕获集成错误。
  • 每个布尔表达式至少需要进行两次测试,并且该数目迅速增长。
  • 单元测试和它们测试的代码一样有漏洞。
  • 将测试链接到几个特定的​​框架或库可能会限制工作流程。
  • 大多数测试是在开发完成后编写的。 真伤心 使用TDD!
  • 也许经过一些重构后,系统将像以前一样工作,但是测试将失败。
  • 开发成本在增长。
  • 人为错误:对损坏的测试进行评论。
  • 人为错误:在代码中添加了变通方法,专门用于通过单元测试。

后者最让我丧命。 (几乎)在每个项目中,直接在工作应用程序的源代码中,我会找到诸如“如果是单元测试,加载代理SQLite数据库,否则加载另一个数据库”或“如果是单元测试,不要发送电子邮件,否则”等行。发送”,依此类推。 如果您的应用程序的体系结构不佳,请不要假装您可以通过良好的测试通过来修复糟糕的软件,否则它不会变得更好。

我经常与同事和客户讨论什么是好的单元测试。 他:

  • 快点
  • 自动化的。
  • 完全控制其所有依赖项。
  • 可靠:它可以以任何顺序启动,而与其他测试无关。
  • 它只能在内存中运行(与数据库无交互,在文件系统中读写)。
  • 始终返回单个结果。
  • 方便阅读和伴奏。
  • 不测试SUT配置(被测系统)。
  • 有一个明确定义的单项任务。
  • 它具有很好的名称(并且易于理解,可以避免为了找出故障而进行调试)。

对于那些在阅读“自动化”之后有些傻笑的人:我并不是说将PHPUnit或JUnit集成到CI管道中。 关键是,如果您更改代码,保存代码并且不知道模块是否通过测试,则它们不是自动化的,而是应该自动化的。 获胜的选择是文件跟踪。

应该对什么进行单元测试?


在正常系统中,需要针对以下内容编写单元测试:

  • 模块-执行任何一项任务(功能,方法,类)的系统中不可分割的隔离部分。
  • 公共方法。
  • 受保护的方法,但仅在极少数情况下且没有人看到时才使用。
  • 错误及其修复。

单元测试的定义取决于编写代码的开发人员。 在PHP中,它几乎总是一个类方法或函数,因为它是一个不可分割的软件,其本身具有意义 。 几次,我看到开发人员如何将一组单方法微型类用作单个模块。 如果最少的功能需要多个对象,则这很有意义。

因此,您自己可以确定什么是适合您的模块。 或者,您可以一个接一个地测试方法,从而使随后使用该代码的人的工作变得更轻松。

如果您不进行单元测试,我建议在下一个大错误之后进行。 检查与之关联的方法,使用正确的参数和结果编写失败的测试,修复错误,然后再次运行单元测试。 如果通过,则可以确保必须在最后一次修复此错误(考虑到您的特定输入方案)。

这种方法使单元测试更容易理解。 分别分析每种方法。 数据提供者可以帮助您确定可能想到的任何情况的输入和输出,因此无论发生什么情况,您都将知道会发生什么。

什么不需要测试


确定您不需要测试有点困难。 我试图编译不需要进行单元测试的元素列表:

  • 功能超出模块范围(!)
  • 模块与其他模块的集成(!)
  • 非绝缘行为(不可模拟的依赖项,真实​​数据库,网络)
  • 私有安全方法。
  • 静态方法。
  • 外部库。
  • 您的框架。

我敢肯定,除静态方法外,单元测试不应应用于上述任何一项。 我喜欢争辩说,从本质上讲,静态意味着程序性,并且在许多情况下,程序性是全局的。 如果静态方法调用了另一个静态方法,则不能覆盖此依赖关系。 这意味着您现在正在隔离测试。 然后,这不再是单元测试。 另一方面,这是代码的一部分,可以自己生存,它是有目的的,并且需要对其进行测试以确保无论代码被测试的那部分调用在愚蠢的系统的哪个部分,它都不会中断。 因此,我相信,如果您确定测试的输出不能被任何其他测试更改,并且该语言或框架允许您进行本机测试,则可以测试静态方法。

如何编写单元测试?


  • 编写适合单元测试的代码,然后对其进行测试。
  • 编写适合单元测试的代码,然后对其进行测试。
  • 编写适合单元测试的代码,然后对其进行测试。

如果“仅测试”还不够,那么laracasts.com上有关于PHP单元测试的非常好的视频。 有很多站点专门从事其他语言的相同任务。 我没有理由解释我如何进行单元测试,因为工具变化很快,阅读本文时,我可以从PHPUnit切换到Kahlan。 还是不行 谁知道

但是,回答第一个问题(如何编写适合于单元测试的代码)要容易得多,而且这种情况不太可能随时间而改变:

  • 实心
  • 干燥
  • 构造函数中缺少新的关键字。
  • 构造函数中没有循环 (如果指定,则包含过渡)。
  • 缺少静态方法,参数,类。
  • 缺少setup()方法:构造后必须完全初始化对象。
  • 缺少单例(全局状态)和其他无法测试的反模式。
  • 缺少万能的对象(God objects)。
  • 缺少具有混合功能的类(混合关注类)。
  • 没有隐藏的依赖项。

现在,了解什么是单元测试,什么不是单元测试,需要什么以及不需要什么测试,单元测试在软件开发生命周期中所处的位置,您将更容易实现它们。 仍然可以找到自己喜欢的框架或库。 如有疑问,请采用事实上的标准框架/语言。

总结:单元测试对于开发人员和企业都非常重要。 需要编写它们,有可靠的方法可以帮助您轻松地通过测试覆盖模块,主要是通过准备模块本身。 但是,如果没有本文中介绍的测试理论的知识,所有这些技术都是没有意义的。 您需要能够将单元测试与其他类型的测试区分开。 而且,如果您对头脑有清晰的了解,那么编写测试将变得更加容易。

Source: https://habr.com/ru/post/zh-CN412695/


All Articles