确定您需要测试的内容以及可以排除的内容。
上一篇文章介绍了测试用例的基础知识及其应包含的内容。本文从技术角度深入探讨了测试用例的创建,并详细介绍了每项测试应包含的内容和注意事项。从本质上讲,您将学习“要测试的内容”或“不应该测试的内容”这些老问题问题的答案。
一般准则和模式
值得注意的是,无论您是执行单元、集成还是端到端测试,具体的模式和要点都至关重要。这些原则可以并且应该同时应用于两种类型的测试,因此是一个很好的起点。
力求简单
在编写测试时,要记住的最重要的一点是保持简单。请务必考虑大脑的容量。主生产代码会占用大量空间,几乎没有增加复杂性的空间。对于测试而言,这一点尤为重要。
如果可用的余量较小,您在测试时可能会比较放松。因此,在测试时要优先考虑简单性,这一点至关重要。事实上,Yoni Goldberg 的 JavaScript 测试最佳做法强调了黄金法则的重要性:测试就应该像是助理,而不是复杂的数学公式。换言之,您应该能够一眼就明白测试的意图。
无论测试的复杂程度如何,所有类型的测试都应力求实现简单性。事实上,测试越复杂,简化就越关键。实现此目的的方法之一是使用扁平测试设计,在这种设计中,测试尽可能简单,并且仅测试必要的内容。这意味着每个测试应只包含一个测试用例,并且测试用例应侧重于测试单个特定功能或特性。
从这个角度来看待问题:在读取失败测试时,应该能够轻易找出问题所在。因此,确保测试简单且易于理解非常重要。这样您就可以在问题出现时快速发现并解决问题。
测试物有所值
扁平测试设计还可以鼓励用户集中注意力,确保测试有意义。请注意,不要只为了测试覆盖率而创建测试,测试应始终有其意义。
不要测试实现详情
测试中的一个常见问题是,测试通常用于测试实现细节,例如在组件或端到端测试中使用选择器。实现细节是指您的代码用户通常不会使用、看到甚至了解的内容。这会导致测试中的两个主要问题:假负例和假正例。
当测试失败时,即使测试的代码正确无误,也会出现假负例。当实现细节因应用代码重构而发生变化时,就可能发生这种情况。另一方面,如果测试通过,就会出现假正例,即使被测试的代码不正确。
此问题的一种解决方法是综合考虑您拥有的不同用户类型。最终用户和开发者采用的方法可能会有所不同,并且他们与代码的互动方式可能会有所不同。在规划测试时,请务必考虑用户将看到的内容或与之互动的内容,并让测试依赖于这些内容,而不是实现细节。
例如,选择不易更改的选择器可使测试更加可靠:使用 data-attributes 而非 CSS 选择器。如需了解详情,请参阅 Kent C. Dodds 的文章就此主题发布,或者敬请关注,后续将发布有关此主题的文章。
模拟:不要失去控制
模拟是单元测试(有时也用于集成测试)中使用的一个宽泛的概念。它涉及创建虚构数据或组件,以模拟可完全控制应用的依赖项。这样即可进行隔离测试。
在测试中使用模拟可以提高可预测性、分离关注点和提高性能。此外,如果您需要进行需要人为干预(例如护照验证)的测试,则必须使用模拟将其隐藏起来。出于上述所有原因,模拟是值得考虑的宝贵工具。
同时,模拟可能会影响测试的准确性,因为它们是模拟,而不是真实的用户体验。因此,在使用模拟和桩时需要注意。
您应该在端到端测试中进行模拟吗?
一般来说,不是。不过,嘲讽有时是一剂救星,因此,我们不能彻底排除这种情况。
设想以下场景:您正在为一项涉及第三方付款服务机构的功能编写测试。您将处于其提供的沙盒环境中,这意味着不会发生任何实际交易。遗憾的是,沙盒发生故障,导致测试失败。此问题需要由付款服务机构来完成。您只能等待提供商解决相关问题。
在这种情况下,减少对您无法控制的服务依赖可能更有益。我们仍然建议您在集成或端到端测试中谨慎使用模拟,因为模拟会降低测试的置信度。
测试细节:正确做法和错误做法
那么,总的来说,测试都包含哪些内容?测试类型之间有什么区别?我们来详细了解一下针对主要测试类型量身定制的一些具体方面。
好的单元测试属于什么?
理想且有效的单元测试应该:
- 专注于特定方面。
- 独立运营。
- 支持小规模场景。
- 使用描述性名称。
- 请遵循 AAA 模式(如适用)。
- 保证全面的测试覆盖范围。
行动 ✅ | 请勿 ❌ |
---|---|
请尽可能缩减测试大小。每个测试用例只测试一项内容。 | 针对大型广告单元编写测试。 |
请务必让测试保持隔离,并模拟单元外您需要的内容。 | 包含其他组件或服务。 |
使测试保持独立。 | 依赖之前的测试或分享测试数据。 |
涵盖不同的场景和路径。 | 尽量避免获得成功或负面的测试。 |
请使用描述性的测试标题,以便一目了然地了解测试内容。 | 仅按函数名称进行测试,结果不够详尽:testBuildFoo() 或 testGetId() 。 |
力求实现良好的代码覆盖率或更广泛的测试用例,尤其是在此阶段。 | 从每个类一直到数据库 (I/O) 级别进行测试。 |
良好的集成测试应具备哪些要素?
理想的集成测试也与单元测试共用一些标准。不过,您还需要注意一些其他事项。出色的集成测试应该:
- 模拟组件之间的交互。
- 涵盖真实场景并使用模拟或桩。
- 考虑性能。
行动 ✅ | 请勿 ❌ |
---|---|
测试集成点:验证每个单元在相互集成时能否优雅地协同工作。 | 单独测试每个单元,这正是单元测试的用途。 |
测试实际场景:使用根据实际数据得出的测试数据。 | 使用自动生成的重复性测试数据或其他不反映实际用例的数据。 |
对外部依赖项使用模拟和存根,以保持对完整测试的控制。 | 创建对第三方服务的依赖项,例如,对外部服务的网络请求。 |
在每次测试前后使用清理程序。 | 忘记在测试中使用清理措施,否则由于缺少适当的测试隔离,可能会导致测试失败或误报。 |
良好的端到端测试属于哪些内容?
全面的端到端测试应该:
- 复制用户互动。
- 包含重要场景。
- 跨越多个图层。
- 管理异步操作。
- 验证结果。
- 考虑效果。
行动 ✅ | 请勿 ❌ |
---|---|
使用由 API 驱动的快捷键。了解详情。 | 对每个步骤使用界面交互,包括使用 beforeEach 钩子。 |
在每次测试之前执行清理程序。比在单元测试和集成测试中更注重测试隔离,因为此时产生副作用的风险更高。 | 每次测试后都忘记清理。如果不清理剩余状态、数据或附带效应,它们会影响稍后执行的其他测试。 |
将端到端测试视为系统测试。这意味着您需要测试整个应用堆栈。 | 单独测试每个单元,这正是单元测试的用途。 |
在测试内尽量少进行模拟或不使用模拟。如果要模拟外部依赖项,请仔细考虑。 | 严重依赖模拟。 |
考虑性能和工作负载,例如不要在同一测试中过度测试大型场景。 | 无需使用快捷方式即可涵盖大型工作流。 |