测试范围

测试因而而异的最常考虑的参数是范围(有时也称为“大小”)。

范围相对于可测试单元而言。侧重于单个此类单元的测试称为单元测试,而同时测试多个单元的测试称为集成测试。大多数测试也可以按测试金字塔的形式排列。

单元是从测试作者的角度任意定义的。例如,在面向对象的编程中,单元通常是类。在本文档中,以及在 Fuchsia 中,通常会提到被测单元是组件,除非另有说明。

在为 Fuchsia 编写测试时,请确保您熟悉编写测试的测试原则最佳实践

测试范围

不同范围的测试相辅相成的优势和劣势。合理搭配测试可以制定出出色的测试计划,让开发者能够放心地进行更改,并快速高效地捕获真实 bug。

在考虑要编写哪些类型的测试以及每种测试的数量时,请想象测试是按金字塔形排列的:

一个金字塔图示,单元测试在宽底,集成测试在中,系统测试在窄尖

许多软件测试出版物提倡混合使用 70% 单元测试、20% 集成测试和 10% 系统测试。Fuchsia 建议在集成测试方面投入更多精力,而忽略其他类型的测试,原因如下:

  • Fuchsia 强调软件的组件化。Fuchsia 上的应用和系统往往会非常广泛地使用组件之间的边界。因此,我们建议您测试这些边界。
  • 借助 Fuchsia 的组件框架,您有时可以轻松地在测试中重复使用生产组件,从而降低开发集成测试的成本。
  • Fuchsia 的测试运行时为集成测试提供了与单元测试相同的隔离级别,使集成测试同样可靠。
  • 组件之间的通信速度比直接方法调用慢几个数量级,但仍然非常快。集成测试的运行速度通常不会比单元测试慢到人为开发者可以察觉到的程度。
  • Fuchsia 上已经有许多集成测试可用作实用示例。

如需详细了解 Fuchsia 中测试金字塔中的各种测试,请参阅以下内容:

还有一些不在测试金字塔范围内的专门测试。如需了解详情,请参阅专门的测试

另请参见:

单元测试

在 Fuchsia 上,在单个组件中完全定义的测试称为单元测试。

这样可以防止测试获得意外功能,防止测试受到测试预期范围之外的系统状态的影响,以及防止测试影响在同一设备上并行或顺序运行的其他测试。

单元测试通过对测试正文中代码的直接调用进行控制。例如,audio_effects_example_test.cc 会在可加载模块中运行代码,该模块会实现音效并验证代码是否有效。

在运行状况良好的测试金字塔中,单元测试是测试的广泛基础,因为单元测试提供了最高的隔离程度和速度最快的测试,反过来又会生成非常可靠且有用的正信号(测试通过)和负信号(测试失败并给出可操作的错误,该错误指示存在真实缺陷)。单元测试还会提供最实用的测试覆盖率信息,并可获得其他附加好处,例如与排错程序结合使用时。因此,任何可通过单元测试满足的测试需求都应通过单元测试满足。

如果只有测试软件包的内容会影响测试能否通过,则单元测试被称为是密封的。

另请参见:

集成测试

在 Fuchsia 上,跨多个组件(这些组件在单个测试软件包中提供)的测试称为集成测试。

在健康的测试金字塔中,集成测试是仅次于单元测试的第二大常见测试类型。Fuchsia 上的集成测试通常速度很快,但不如单元测试。与单元测试一样,Fuchsia 上的集成测试会与系统的其余部分隔离,不受外部影响。与单元测试相比,集成测试在定义和驱动方面更为复杂。因此,如果单元测试无法满足某项测试需求,但集成测试可以满足,则应使用集成测试来满足该需求。

与其他平台相比,集成测试在 Fuchsia 中发挥着更重要的作用。 这是因为 Fuchsia 鼓励软件开发者将其代码分解为多个组件,以实现平台的安全可更新目标,然后提供强大的组件间通信机制,例如通道FIDL

在集成测试中,创建 realm 的方式与单元测试类似,但随后会在该 realm 中暂存多个组件,并使这些组件相互交互。测试由测试领域中的组件之一驱动,该组件还会确定测试是通过还是失败,并提供额外的诊断信息。例如,字体服务器集成测试会测试将 fuchsia.pkg.FontResolver FIDL 协议作为协议功能公开的字体解析器组件,然后通过调用该组件与客户端的合约中的方法并验证预期响应和状态转换来验证该组件。

集成测试由测试驱动程序控制。测试驱动程序会设置一个或多个受测组件,对其进行演练,并观察结果,例如使用受支持的接口和其他协定查询这些组件的状态。

在测试领域中,标准 capability 路由的运作方式与在任何其他领域中的运作方式完全相同。在测试 realm 中,capability 路由通常用作依赖项注入机制。例如,如果被测组件依赖于不在测试范围内的另一个组件提供的功能,那么可以针对其他组件(“测试替身”)或在测试领域内独立运行的生产组件的单独实例进行测试。

如果只有测试软件包的内容会影响测试通过还是失败,则称集成测试为封闭测试。

另请参见:

  • 复杂拓扑和集成测试:如何静态定义包含多个组件且组件之间具有路由功能的测试 Realm。
  • Realm builder:一个集成测试辅助程序库,用于在运行时构建各个测试用例中的测试领域和模拟组件。

系统测试

在 Fuchsia 上,范围不局限于单个软件包及其包含的一个或多个组件的测试称为系统测试。此类测试的范围并未严格定义为组件或软件包,并且可能涵盖给定配置中构建的整个系统。

系统测试有时称为关键用户历程 (CUJ) 测试或端到端 (E2E) 测试。有些人可能会说,如果测试范围甚至超出了单个 Fuchsia 设备(例如,针对远程服务器进行测试,或者在存在特定 Wi-Fi 接入点或其他已连接的硬件的情况下进行测试),则此类测试属于端到端测试。

在运行状况良好的测试金字塔中,系统测试是金字塔的窄头,这表示系统测试少于其他范围内的测试。系统测试通常比单元测试和集成测试慢得多,因为它们会运行更多代码,并且具有更多依赖项,其中许多依赖项是隐式定义的,而不是显式声明的。系统测试无法提供固有的隔离程度,并且会受到更多副作用或意外干扰的影响,因此通常不如其他类型的测试可靠。由于系统测试所执行代码的范围未定义,Fuchsia 的 CI/CQ 不会从端到端测试收集测试覆盖率(收集覆盖率会产生不稳定或不稳定的结果)。出于类似原因,sanitizer 不支持端到端配置。因此,如果单元测试或集成测试无法获得边际好处,您应该编写系统测试。

其他测试

Fuchsia 中提供了各种专用测试:

内核测试

由于涉及的特殊要求和限制,因此需要特别注意测试内核。内核测试涵盖内核的表面:系统调用、内核机制(例如句柄权限或信号对象)、vDSO ABI 和用户空间引导,以及所有重要的实现细节。

内核的大多数测试都在用户空间中运行,并从外部测试内核 API。这些测试称为“核心用户模式测试”。例如,TimersTest 用于测试内核的计时器 API。与常规测试相比,这些测试的编写难度更高:仅支持某些编程语言、测试之间的隔离边界较弱、测试无法并行运行、无法在不重新启动和重新铺路的情况下更新测试,并且还存在其他约束条件和限制。通过这些权衡,测试对其运行时环境的假设会减少,这意味着即使在没有组件框架或高级网络功能的最基本 build 配置(启动 build)中,这些测试也能运行。您可以运行这些测试,使其在用户模式下不运行任何其他程序,这有助于隔离内核 bug。

某些内核行为无法从外部进行测试,因此需要使用编译到内核映像并在内核空间中运行的代码进行测试。这些测试更难以编写和进行问题排查,但对内核内部结构的访问权限最大,并且即使在用户空间引导中断时也可以运行。有时,这对于测试来说是必要的。例如,timer_tests.cc 会测试计时器截止期限,而仅通过从用户空间调用内核 API 无法准确地测试这些期限,因为内核在合并从用户空间设置的计时器时会预留一些空闲时间

兼容性测试

兼容性测试(有时称为一致性测试)将正确性的概念扩展为满足给定规范并随着时间的推移遵循该规范。兼容性测试在某些合同条款中有所规定,并且可针对该合同的多个实现运行,以确保任意数量的实现兼容。

例如:

  • Fuchsia 兼容性测试 (CTF) 是用于验证 Fuchsia 系统接口的测试。CTF 测试可以针对不同版本的 Fuchsia 运行,以确保它们保持兼容性或检测破坏性更改。最终,CTF 测试将涵盖 Fuchsia SDK 的整个 Surface。
  • FIDL 使用特定的二进制格式(或线格格式)来编码在组件之间通过通道交换的 FIDL 消息。FIDL 客户端和服务器端绑定和库以许多语言(例如 C、C++、Rust、Dart 和 Go)提供。FIDL 兼容性测试Golden FIDL (GIDL) 测试可确保不同的实现具有二进制兼容性,这样无论开发者选择哪种编程语言,客户端和服务器都可以正确且一致地相互通信。
  • Fuchsia Inspect 使用特定二进制格式来编码诊断信息。检查功能支持多种语言,并且底层二进制格式可能会随时间而变化检查验证器测试,确保所有读取器/写入器实现之间的二进制兼容性。

性能测试

性能测试会运行特定工作负载(例如合成基准测试或 CUJ),并衡量测试期间的性能指标,例如完成时间或资源使用情况。

性能测试可能会确保满足特定阈值(例如,帧以 60 FPS 的一致速率渲染),也可能只是收集性能信息并将其作为结果显示。

例如:

  • 您可以在端到端性能测试目录中找到许多性能测试和性能测试实用程序。这些测试涵盖了内核启动时间、触控输入延迟时间、Flutter 渲染性能以及对性能敏感的代码的各种微基准等一系列目标。
  • CPU 性能监控使 CPU 性能计数器可用于高频低开销性能测试。
  • Perfcompare 是一个框架,用于比较给定更改前后给定基准的结果。这对于衡量性能改进或回归非常有用。
  • FIDL 基准测试包含适用于 FIDL 生成的代码的各种大小的基准测试。
  • 网络堆栈基准测试使用热点代码的微基准测试网络堆栈的性能。

压力测试和长时测试

压力测试会以极高负载的方式运行系统,但在系统协定中,这种情况属于正常现象。其目标是展示系统应对压力的弹性,或者找出只有在压力下才会发生的 bug。

在压力测试中,受测系统会与测试工作负载组件隔离。测试工作负载组件可以执行任何操作,包括崩溃,而不会使被测系统崩溃。

例如,minfs 压力测试会反复对 MinFS 分区执行许多操作。该测试可以验证操作是否成功、观察到的结果是否符合预期,以及 MinFS 是否会崩溃或出现文件系统损坏等其他错误。

有意长时间运行系统的测试是一种称为长时测试的压力测试。在长寿命测试中,时间跨度与系统上的负载或压力密切相关。例如,如果系统成功完成 10,000 次用户体验历程,则属于压力测试;如果系统在 20 小时内循环完成相同的用户体验历程,并且始终正确且响应迅速,则也属于长效性测试。

另请参见:

模糊测试

fuzz 工具是指尝试与待测代码进行有效交互的程序,这类交互会导致错误情况(例如崩溃)。模糊测试由伪随机行为决定,有时通过插桩(搜索覆盖新代码路径的输入)或其他明智策略(例如预生成的字典或机器学习)引导。

经验表明,模糊测试工具可以发现独特的高严重性 bug。例如,reader_fuzzer.cc 在 Inspect 读取器代码中发现了会导致运行时崩溃的 bug,即使此代码已通过大量测试和过滤器。

另请参见: