测试范围

测试中考虑最多的参数是范围(有时也称为“大小”)。

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

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

在为 Fuchsia 编写测试时,您需要确保自己熟悉测试原则和编写测试的最佳实践

测试范围

不同范围的测试可以互补彼此的优势和劣势。通过合理搭配各种测试,可以制定出色的测试方案,让开发者能够放心地进行更改,并快速高效地发现实际 bug。

在考虑要编写哪种测试以及每种测试的数量时,请将测试想象成一个金字塔:

金字塔图示,底部宽阔,为单元测试;中间为集成测试;顶部狭窄,为系统测试

许多软件测试出版物都建议采用 70% 单元测试、20% 集成测试和 10% 系统测试的组合。Fuchsia 建议投入更多资源进行集成测试,而减少其他类型的测试,原因如下:

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

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

此外,还有一些不属于测试金字塔的专业测试。 如需了解详情,请参阅专业测试

另请参见:

单元测试

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

单元测试可以在充当沙盒的隔离 realm 中运行。这样可防止测试获取意外的功能、受到测试预期范围之外的系统状态的影响,以及影响在同一设备上并行或按顺序运行的其他测试。

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

在健康的测试金字塔中,单元测试是广泛的测试基础,因为单元测试可提供最高程度的隔离,并且是速度最快的测试,这反过来会产生非常可靠且有用的正信号(测试通过)和负信号(测试失败,并显示可据以采取行动的错误,表明存在实际缺陷)。单元测试还可以生成最具实用价值的测试覆盖率信息,并享有其他附带好处,例如与清理器结合使用时。因此,任何可以通过单元测试满足的测试需求都应通过单元测试来满足。

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

另请参见:

集成测试

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

在健康的测试金字塔中,集成测试是仅次于单元测试的第二大最常见测试类型。Fuchsia 上的集成测试通常速度很快,但不如单元测试快。Fuchsia 上的集成测试与单元测试一样,不受系统其余部分的外部影响。集成测试比单元测试更难定义和驱动。因此,任何无法通过单元测试满足但可以通过集成测试满足的测试需求都应通过集成测试来满足。

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

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

集成测试由测试驱动程序控制。测试驱动程序会设置一个或多个受测组件,对其进行测试,并通过使用这些组件支持的接口和其他合约查询其状态等方式来观察结果。

在测试 realm 中,标准 功能路由 在测试 realm 中,功能路由通常用作依赖项注入的机制。例如,如果被测组件依赖于测试范围之外的另一个组件提供的功能,则可以针对不同的组件(“测试替身”)或在测试领域内以隔离方式运行的生产组件的单独实例进行测试。

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

另请参见:

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

系统测试

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

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

在健康的测试金字塔中,系统测试是金字塔的窄尖,表明系统测试的数量少于其他范围的测试。系统测试通常比单元测试和集成测试慢得多,因为它们会执行更多代码并具有更多依赖项,其中许多依赖项是隐式定义而非显式声明的。系统测试本身不具备隔离性,容易受到更多副作用或意外干扰,因此通常不如其他类型的测试可靠。由于系统测试所覆盖的代码范围未定义,因此 Fuchsia 的 CI/CQ 不会从 E2E 测试中收集测试覆盖率(收集覆盖率会产生不稳定或不可靠的结果)。出于类似原因,清理器不支持 E2E 配置。因此,当单元测试或集成测试无法获得边际效益时,您应编写系统测试。

其他测试

Fuchsia 中有各种专门的测试:

内核测试

由于内核测试涉及独特的要求和限制,因此值得特别关注。内核测试涵盖内核的表面:系统调用、句柄权限或信号对象等内核机制、vDSO ABI 和用户空间启动,以及任何重要的实现细节。

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

某些内核行为无法从外部进行测试,因此需要使用编译到内核映像中并在内核空间中运行的代码进行测试。这些测试更难编写和排查问题,但可以最大程度地访问内核内部,即使在用户空间引导加载程序损坏时也可以运行。有时,这对于测试是必需的。例如,timer_tests.cc 会测试计时器截止时间,但仅通过从用户空间调用内核 API 无法精确测试该截止时间,因为内核会在合并从用户空间设置的计时器时预留一些裕度

兼容性测试

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

例如:

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

性能测试

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

性能测试可确保达到某个阈值(例如,以 60 FPS 的稳定速率渲染帧),也可仅收集性能信息并将其作为结果呈现。

例如:

  • 许多性能测试和性能测试实用程序都可以在端到端性能测试目录中找到。这些测试涵盖了各种目标,例如内核启动时间、触控输入延迟、Flutter 渲染性能,以及对性能敏感的代码的各种微基准。
  • Perfcompare 是一个框架,用于比较给定基准在给定更改前后的结果。它有助于衡量性能改进或退化。
  • FIDL 基准包含各种大小的 FIDL 生成代码基准。
  • Netstack 基准使用热门代码的微基准测试网络堆栈的性能。

压力测试和长时测试

压力测试会以相当于极高负载的方式来测试系统,但这些方式在系统的合同中是允许的。其目的是展示系统在压力下的恢复能力,或发现仅在压力下才会出现的 bug。

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

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

有意长时间运行系统的测试是一种压力测试,称为长时测试。在耐久性测试中,时间跨度是系统负载或压力固有的。例如,如果某个测试让系统成功完成用户历程 10,000 次,则该测试属于压力测试;如果某个测试让系统循环完成同一用户历程 20 小时,并且系统始终保持正确且响应迅速,则该测试也属于耐久性测试。

另请参见:

模糊测试

模糊测试器是一种程序,它会尝试与被测代码产生有效的交互,从而导致错误情况(例如崩溃)。模糊测试由伪随机行为指导,有时通过插桩(寻找覆盖新代码路径的输入)或其他知情策略(例如预生成的字典或机器学习)指导。

经验表明,模糊测试工具可以发现独特的高严重性 bug。例如,reader_fuzzer.cc 在检查阅读器代码中发现了会在运行时导致崩溃的 bug,即使该代码已经过广泛的测试和清理。

另请参见: