从技术角度来看,测试或不测试

确定您需要测试的内容以及可以排除的内容。

上文介绍了测试用例的基础知识以及它们应包含的内容。本文将从技术角度深入探讨测试用例的创建,详细介绍每个测试应包含的内容以及应避免的内容。本质上,您将了解“测试什么”或“不测试什么”这一老生常谈问题的答案。

要测试或不测试的内容。

一般准则和模式

值得注意的是,无论您是进行单元测试、集成测试还是端到端测试,特定模式和测试点都至关重要。这些原则可以而且应该应用于这两种类型的测试,因此是良好的起点。

力求简单易行

在编写测试时,最重要的一点是力求简单。请务必考虑大脑的容量。主要生产代码会占用大量空间,因此几乎没有空间容纳额外的复杂性。这在测试时尤为重要。

如果可用空间较少,您在测试时可能会更加放松。因此,在测试中,务必优先考虑简单性。事实上,Yoni Goldberg 的 JavaScript 测试最佳实践强调了黄金法则的重要性:测试应该像助理一样,而不是复杂的数学公式。换句话说,您应该能够一目了然地了解测试的意图。

不要让测试变得复杂,测试不应给人这样的感觉。

无论测试类型有多复杂,您都应力求简化。事实上,测试越复杂,简化测试就越重要。实现此目标的一种方法是采用扁平的测试设计,即尽可能简化测试,并仅测试必要的内容。这意味着,每个测试都应仅包含一个测试用例,并且测试用例应侧重于测试单个特定功能。

从这个角度来看:阅读失败的测试时,应该能够轻松找出问题所在。因此,请务必使测试简单易懂。这样,您就可以在问题出现时快速发现并解决问题。

测试值得测试的内容

扁平的测试设计还能鼓励您专注于测试,并有助于确保测试有意义。请注意,您不应仅仅为了提高代码覆盖率而创建测试,测试应始终有目的。

请勿测试所有内容。

请勿测试实现详情

测试中的一个常见问题是,测试通常旨在测试实现细节,例如在组件或端到端测试中使用选择器。实现细节是指代码用户通常不会使用、看到或甚至不知道的内容。这可能会导致测试中出现两个主要问题:假负例和假正例。

当测试失败时,即使被测代码正确无误,也会出现假负例。如果应用代码因重构而导致实现细节发生变化,就可能会发生这种情况。另一方面,如果测试通过,但被测代码不正确,则会出现假正例。

解决此问题的一个方法是考虑您拥有的不同类型的用户。最终用户和开发者的做法可能不同,他们与代码的互动方式也可能不同。在规划测试时,请务必考虑用户将看到或与之互动的内容,并让测试依赖于这些内容,而不是实现细节。

例如,选择不易发生变化的选择器(例如 data-attributes 而不是 CSS 选择器)可以提高测试的可靠性。如需了解详情,请参阅 Kent C. Dodds 的文章了解详情,或稍后关注我们,我们将推出一篇关于此主题的文章。

模拟:不要失去控制

模拟是一个广泛的概念,在单元测试中使用,有时也用于集成测试。它涉及创建虚构数据或组件,以模拟对应用具有完全控制权的依赖项。这样可以进行隔离测试。

在测试中使用模拟对象可以提高可预测性、关注分离性和性能。如果您需要进行需要人工参与的测试(例如护照验证),则必须使用模拟内容来隐藏测试。出于所有这些原因,模拟是值得考虑的有用工具。

同时,模拟可能会影响测试的准确性,因为它们是模拟内容,而不是真实的用户体验。因此,在使用模拟对象和桩时,您需要多加注意。

是否应在端到端测试中进行模拟?

一般来说,不需要。不过,模拟有时会派上用场,因此我们不妨不完全排除这种可能性。

假设您要为涉及第三方付款服务提供商的功能编写测试。您目前在他们提供的沙盒环境中,这意味着不会发生任何真实交易。很抱歉,沙盒出现故障,导致您的测试失败。付款服务提供商需要解决此问题。您只能等待提供商解决此问题。

在这种情况下,减少对您无法控制的服务的依赖可能更有益。不过,建议您在集成或端到端测试中谨慎使用模拟,因为这会降低测试的置信度。

测试具体信息:注意事项

总而言之,测试包含哪些内容?测试类型之间有区别吗?我们来详细了解一些针对主要测试类型量身定制的具体方面。

什么是有效的单元测试?

理想且有效的单元测试应满足以下条件:

  • 专注于特定方面。
  • 独立运营。
  • 涵盖小规模场景。
  • 使用描述性名称。
  • 遵循 AAA 模式(如果适用)。
  • 保证全面的测试覆盖率。
正确做法 ✅ 不应❌
尽可能缩减测试规模。每个测试用例测试一项内容。 编写针对大型单元的测试。
始终将测试隔离,并模拟单元之外所需的各项内容。 添加其他组件或服务。
使测试保持独立。 依赖于之前的测试或共享测试数据。
涵盖不同的场景和路径 最多只进行理想路径测试或负例测试。
使用描述性测试标题,以便您立即了解测试内容。 仅按函数名称进行测试,因此描述性不足:testBuildFoo()testGetId()
力求实现良好的代码覆盖率或更广泛的测试用例,尤其是在此阶段。 从每个类一直测试到数据库 (I/O) 级别。

什么是良好的集成测试?

理想的集成测试也与单元测试共享一些标准。不过,您还需要考虑以下几点。出色的集成测试应具有以下特点:

  • 模拟组件之间的交互。
  • 涵盖真实场景,并使用模拟对象或桩。
  • 考虑性能。
正确做法 ✅ 不应❌
测试集成点:验证各个单元在集成后能否顺利协同工作。 单独测试每个单元,这就是单元测试的用途。
测试真实场景:使用从真实数据派生的测试数据。 使用重复的自动生成的测试数据或不反映实际用例的其他数据。
对外部依赖项使用模拟对象和桩,以便保持对完整测试的控制。 创建对第三方服务的依赖项,例如对外部服务的网络请求。
在每次测试前后使用清理例程。 忘记在测试中使用清理措施,否则可能会因缺少适当的测试隔离而导致测试失败或假正例。

什么是良好的端到端测试?

全面的端到端测试应:

  • 重现用户互动。
  • 涵盖重要场景。
  • 跨多个图层。
  • 管理异步操作。
  • 验证结果。
  • 考虑效果。
正确做法 ✅ 不应❌
使用 API 驱动的快捷方式。了解详情 对每个步骤(包括 beforeEach 钩子)使用界面互动。
在每次测试之前使用清理例程。与单元测试和集成测试相比,您需要更加注意测试隔离,因为发生副作用的风险更高。 忘记在每次测试后进行清理。如果您不清理剩余状态、数据或副作用,它们将影响稍后执行的其他测试。
将端到端测试视为系统测试。这意味着您需要测试整个应用堆栈。 单独测试每个单元,这就是单元测试的用途。
尽量减少在测试中使用模拟,或者完全不使用模拟。请仔细考虑是否要模拟外部依赖项。 过度依赖模拟对象。
考虑性能和工作负载,例如,不要在同一测试中过度测试大型场景。 在不使用快捷方式的情况下涵盖大型工作流。