运行测试的位置

通常可以通过以下方式运行自动化测试:手动运行脚本或使用测试框架中的辅助程序(通常称为“测试运行程序”)来查找并运行测试。不过,您可能并不总是需要手动运行脚本。您可以通过多种方式运行测试,以便在开发生命周期的不同阶段提供反馈和提升信心。

必备脚本

Web 项目通常有一个配置文件(即 package.json 文件),该文件通过 npm、pnpm、Bun 或类似工具进行设置。此配置文件包含项目的依赖项和其他信息,以及帮助程序脚本。这些辅助脚本可能包括如何构建、运行或测试您的项目。

package.json 中,您需要添加一个名为 test 的脚本,用于描述如何运行测试。这一点很重要,因为在使用 npm 或类似工具时,“test”脚本具有特殊含义。此脚本可以仅指向单个会抛出异常的文件(类似于 node tests.js),但我们建议您使用它指向已确立的测试运行程序。

如果您使用 Vitest 作为测试运行程序,您的 package.json 文件将如下所示:

{
  "name": "example-project",
  "scripts": {
    "start": "node server.js",
    "test": "vitest --run"
  }
}

使用此文件运行 npm test 会运行一次 Vitest 的默认测试集。在 Vitest 中,默认查找以“.test.js”或类似文件结尾的所有文件并运行这些文件。根据您选择的测试运行程序,该命令可能会略有不同。

在本课程中,我们选择使用 Vitest 这一越来越受欢迎的测试框架作为示例。如需详细了解此决定,请参阅将 Vitest 用作测试运行程序。但请务必注意,测试框架和运行程序(即使是跨语言进行测试)往往会有共同的语言。

手动测试调用

当您正在处理代码库时,手动触发自动化测试(例如使用上一个示例中的 npm test)可能会非常实用。在开发某项功能时编写测试有助于您了解该功能的运作方式,而这涉及到测试驱动开发 (TDD) 的概念。

测试运行程序通常有一个简短的命令,您可以调用它来运行部分或全部测试,还可能会有一个 Watcher 模式,该模式会在您保存测试时重新运行测试。这些在开发新功能时都很有用,它们旨在让您轻松编写新功能和/或测试,并快速获得反馈。例如,Vitest 默认在 watcher 模式下运行:vitest 命令会监视更改,并重新运行找到的任何测试。我们建议您在编写测试时让另一个窗口保持打开状态,以便在开发测试时快速获得反馈。

某些运行程序还允许您在代码中将测试标记为 only。如果您的代码包含 only 测试,则在运行测试时,只有这些测试才会触发,从而使测试开发更快、更轻松地进行问题排查。即使所有测试都能快速完成,使用 only 也可以降低开销,并消除运行与所处理功能或测试无关的测试所产生的干扰。

对于小型项目,特别是只有一位开发者的项目,您可能还需要养成定期运行代码库的整个测试套件的习惯。如果您的测试规模较小且可快速完成(所有测试在几秒钟内完成),此功能尤为有用,这样您便可以确保一切正常,然后再继续。

在提交前或审核过程中运行测试

许多项目会选择确认在将代码合并回其 main 分支时代码库正常运行。如果您刚开始测试,但过去为开源项目做过贡献,那么您可能已经注意到,拉取请求 (PR) 流程的一部分会确认项目的所有测试均通过,这意味着您激动人心的新贡献没有对现有项目产生负面影响。

如果您在本地运行测试,则项目的在线代码库(例如 GitHub 或其他代码托管服务)不会知道您的测试已通过,因此以提交前任务的形式运行测试,可让所有贡献者清楚地知道一切正常。

例如,GitHub 将这些操作称为“状态检查”,您可以通过 GitHub 操作添加这些检查。GitHub 操作从本质上就是一种测试:每个步骤都必须成功(而非失败或抛出 Error),操作才能通过。您可以将 Action 应用于项目的所有 PR,项目可以要求 Action 必须先传递,然后才能贡献代码。GitHub 的默认 Node.js 操作会运行 npm test 作为其步骤之一。

GitHub Actions 测试流程的屏幕截图。
GitHub Actions 测试流程的屏幕截图。

这种测试方法会尝试接受无法成功运行测试的代码,以确保代码库始终处于“绿色”状态。

在持续集成环境中运行测试

接受绿色 PR 后,大多数代码库会根据项目的 main 分支(而不是之前的 PR)再次运行测试。此过程可能会立即发生,也可能会定期(例如,每小时或每晚)发生。这些结果通常作为持续集成 (CI) 信息中心内的一部分显示,该信息中心内会显示整体项目健康状况。

此 CI 步骤似乎是多余的,对于具有小型代码库的项目而言更是如此。这些测试在审核期间通过了审核,因此在发生更改后应该能够通过这些测试。不过,情况并非总是如此!即使在成功生成绿色结果后,您的测试也可能会突然失败。造成这种情况的一些原因包括:

  • 多项更改被“同时”接受(有时称为竞态条件),它们会以未经测试的细微方式相互影响。
  • 您的测试不可重现,或者测试“不稳定的”代码;在不更改代码的情况下,测试既能通过,也能失败。
    • 如果您依赖于代码库外部的系统,则可能会发生这种情况。对于代理,请想象要测试是否 Math.random() > 0.05,这会有 5% 的几率随机失败。
  • 有些测试的成本太高,以致无法在每个 PR 上运行,如端到端测试(自动化测试的类型部分对此进行了详细介绍),并且这些测试可能会随时间中断,而不会始终发出提醒。

所有这些问题都无法解决,但值得意识到的是,测试和一般软件开发永远不是一门精确的科学。

回滚期间的插曲

如果测试作为持续集成的一部分运行,即使在状态检查过程中运行测试,构建也有可能最终处于“红色”状态,或表示测试失败的其他状态。如前所述,发生这种情况的原因有很多,包括测试提交时出现的竞态条件或不稳定的测试。

对于规模较小的项目,您的本能可能会将其视为危机!停止所有操作、回滚或还原违规更改,并恢复到已知的良好状态。这可能是一种有效的方法,但请务必注意,测试(以及一般来说是软件!)只是一种实现方法,而并不是实现目标。您的目标可能是编写软件,而不是让所有测试都通过。相反,您可以通过向前滚动,在重大更改之后发布修复失败测试的另一项更改。

另一方面,您可能看到或处理过存在于永久性损坏状态的大型项目。更糟糕的是,大型项目的测试不稳定,该测试会经常中断,导致开发者出现闹钟疲劳。这通常是领导者需要解决的一个存在性问题:这些测试甚至可能因被视为“阻碍开发”而被关闭。

此问题没有快速修复的方法,但有助于提高编写测试的信心(提升性能),并缩小测试范围(简化),以便更轻松地识别失败。与一个难以维护并试图一次性完成所有任务的大型端到端测试相比,增加组件测试集成测试自动化测试类型中详细了解类型)能够提供更高的置信度。

资源

检查您的掌握程度

npm 和类似程序在测试时寻找的特殊脚本的名称是什么?

check
测试
提交前
verify