开发人员关于测试的热门问题

本文不是编写测试的理论指南,也不是如何在特定堆栈中使用工具的方法指南,而是一系列常见的问题,有时甚至是许多尚未形成的问题,我将尝试给出答案。 这些问题的源头是同事,面试和相识中的双方人员,并且根据他人的材料和经验,答案将是主观的,简短的而非详尽的。 本文的目标读者是开发人员,他们取得了一定的成功,或者至少尝试编写测试,但是在编写测​​试时遇到了一些困难。


为了扩大读者的范围,我尝试不附加特定的语言,但是我会立即保留使用PHPUnit在PHP生态系统中工作的权利,因此,我的某些结论可能不适用于其他生态系统。 在选择问题和写作时,我专注于许多报告和文章,并将它们用作参考。


撰写本文的原因是最近的文章“ PHPUnit。 通过拖网Trawl)破坏“教义实体管理器 ”,我也将讨论其中的一些。


问题清单:


  • 编写还是不编写测试?
  • 如果没有分配时间进行测试?
  • 测试类型,如何选择?
  • 为什么我很难长时间编写测试?
  • 如何测试私有方法?
  • 如何编写集成测试? 如何测试基础?
  • 如何:集成还是功能?
  • 如何处理外部依赖关系?
  • 如何简化测试和测试主题之间的导航?
  • 我应该使用TDD吗?
  • 还有什么可以用来改进代码的?

编写还是不编写测试?


我在这个问题上看到了很多意见,但是我得出了自己的结论:我需要专注于业务需求。


对于需要在几个小时内完成的一段代码,不需要进行测试。 另一方面,对于一个拥有数百名员工的大型公司项目,需要进行所有流行的测试。 这些极点之间的所有事物都应视为特例,以评估某些类型的测试的成本:有了测试,应该比没有测试少。 就个人而言,我甚至为一个持续数周的小型CRUD项目编写烟雾测试,因为这种距离已经带来了好处并降低了开发成本。


简短抽象的测试优势:


  • 由于及早发现而大大降低了修复错误的成本。
  • 固定合同。
  • 低级文档。
  • 检测建筑问题。

因此,如果您的项目不是来自多个文件的一次性脚本,那么我绝对建议编写测试。 因此,标题中的问题应重新表述:“测试应写到什么程度,哪些要写?” 关于它进一步。


如果没有分配时间进行测试?


这是一个非常有争议的问题,因此我将再次保留我仅谈论主观意见的权利,同时我会尽力做到这一点。


编写测试与思考,编码或调试一样重要。 测试与业务逻辑或控制器的代码相同。 但是您对此有任何疑问吗? 您没有为编写控制器设置单独的任务,也没有与经理协调吗?


不必花费额外的时间编写单元测试和集成测试。 测试被编写为主要任务的一部分。 作为项目资料库的所有者和胜任的人员,项目的技术负责人决定编写测试的方式,内容和种类,重点放在业务任务上,而不是虚张声势,而是降低开发成本。


但是,可能会发生这样的情况:客户不相信测试,并相信在他们的帮助下您不会省钱,而只是浪费时间。 他付钱并设定条件,但同样成功,他可以禁止您在IDE中使用深色主题,或禁止使用五个空格作为缩进。 并且,如果没有讨论条件或讨论陷入僵局,那么您的选择是接受这样的条件或拒绝。 具有建议性的后果。


我将保留一点,只有在您可以编写测试,充分评估测试成本和估计投资回报率的情况下,后者才是正确的。 如果您想学习如何在一个短期项目上以牺牲雇主的代价来编写它们,而他反对这样做,那么我站在他这一边。


测试类型,如何选择?


迈克尔·科恩(Michael Cohen)测试金字塔


您可以看一下Mike Cohen的测试金字塔:作为开发人员,我们对最低的两个测试级别感兴趣。 应该将最大的精力放在单元测试上,这是最便宜的(便宜是指我花费在开发和支持上的时间),这样的测试实现起来非常简单,运行起来非常简单,而且工作很快。


但是单元测试是否总是适用或可取的? 我相信,对于原始CRUD,此类测试将花费太多时间而几乎没有影响,并且不能保证任何事情。 尝试测试存储库(DataMapper中的存储库),然后回答问题,它给了我们什么。 但是对于各种计算器,这种方法将是理想的。


始终尝试在可能的情况下编写单元测试,但不要测试无用的测试内容。


如果这些是不同堆栈上的不同项目,那么如何测试后端和前端的联合工作? 就像后端和移动应用程序一样:这是系统测试,它应该对QA和DevOps工程师而不是开发人员感兴趣(好吧,只有在您没有真正的残酷混乱的情况下,唯一可用的角色是全栈开发人员)。


此外,就时间和基础架构而言,就开发和支持而言,系统测试是最昂贵的。 与之相关的问题的解决方案已经超出了“线性”开发人员的能力范围,其应用和范围的问题应由技术主管,该领域的负责人以及企业共同决定。


为什么我很难长时间编写测试?


有时很难编写测试,因为代码还没准备好。 一堆小怪,其中一些会给其他人,也是后果。


不要使用注册表,单调,定位符,这会使您的生活大大复杂化。 这是我上面提到的文章的主要主张。 使用DI,并立即在服务中实现必要的存储库。 遵守得墨meter耳的定律,以免产生莫卡斯链。 使用TDD方法尝试一些工作。


将测试视为一流的代码,并遵守与业务逻辑相同的高质量代码。 测试代码质量差会随着时间的推移降低生产率。


如何测试私有方法?


没办法 我们正在测试合同-该模块提供给其他模块的接口。 我们不需要测试内部实现,它可以更改。


如果测试内部存在一些复杂的逻辑,并且测试合同变成大量输入,而内部却发生了奇怪的事情,该怎么办? 有必要重构代码,将其分配到不同的类中,这些私有方法将在其中公开,而它们已经编写了单元测试。 而且,如果没有测试,这些地方将很难被发现,这将增加支持此类代码的成本。


如何编写集成测试? 如何测试基础?


使用固定装置。 探索堆栈上的流行工具。
不要写通用的大型灯具,请使用最少的灯具。 如果您需要某种状态的对象进行测试,则不要在不需要这种状态的其他地方重复使用这些固定装置,否则测试支持将变得更加复杂:为某些测试更改它们会破坏其他测试并花费额外的时间。


有一些集成测试没有经历从请求到响应的完整周期,并且仅在组件内部检查类的交互。 如果他们没有直接使用数据库的功能,则可以制作一组数据和/或mok并将其输入到输入中。 在这种情况下,您可以避免使用固定装置,并将此类集成测试的复杂性和执行时间降低到模块化级别。


如何:集成还是功能?


集成测试是隔离方面的测试级别之一。 功能测试-合规性测试。 这些定义位于不同的平面上,您不能在它们之间使用等号,但实际上,在开发人员之间的对话中,它们的含义相同,尽管这是不正确的。


如何处理外部依赖关系?


我们将外部依赖项替换为moki。 不仅用于单元测试,还用于集成测试。


例如,我们使用HTTPS客户端通过Guzzle类访问某些API。 如果在被测试的类中创建此类的实例,将很难替换它,但是解决方案将非常简单:我们在构造函数中实现了这样的客户端,并且在测试过程中,我们将其替换为moch。


如何简化测试和测试主题之间的导航?


如果使用命名标准,现代开发工具可以跟踪测试或测试类的位置。 为了便于导航,您可以在JetBrains产品中使用键盘快捷键Ctrl + Shift + T,此外,如果测试不存在,则会提示您创建测试并制作线框。


有时,您需要几种不同的测试类或方法来测试主题,在这种情况下,您需要帮助IDE,例如,对于PHPUnit,添加@covers批注。


我应该使用TDD吗?


TDD是一种开发方法论,其中先要迭代地编写测试,然后再编写代码。 试试看绝对值得,它将教您如何编写经过良好测试的代码。 但是,这种方法并未得到广泛使用,但存在问题。 这些问题之一是您编写的测试不是用经典方法编写的。 而且还需要支持此类测试,花费大量时间,并且从中获得的好处很小。 例如,对setter的测试或对类的测试,除了依赖项调用之外,什么都没有发生。 最好通过集成测试来检查这些地方。


还有什么可以用来改进代码的?


静态分析工具是提高代码质量的另一种方法。 他们的结果与测试部分重叠,但是他们经常发现未找到测试,尤其是覆盖范围较差的情况。 它们无疑具有优势-您可以在IDE中立即看到结果并立即获得反馈。


这并不意味着仅将它们保留在IDE上就足够了,我强烈建议在CI上使用它们以查看潜在的错误并且不要破坏此类代码。
我还建议向IDE和CI添加代码样式检查工具支持。 错误通常隐藏在粗心的代码中,而此类工具可让您找到此类区域。


进一步研究的材料


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


All Articles