测试环境

什么是测试中所述,JavaScript 中的测试从本质上来说只是我们确认已成功运行的代码,也就是说,不会抛出 Error。不过,这种定义过于简化的一种方式是,它没有考虑我们运行代码的位置及其测试环境。

测试环境大致可视为两个组成部分:用于运行测试的运行时环境(例如 Node 或浏览器),以及可供您使用的 API。

运行时环境

Node 等运行时或 Deno 或 Bun 等类似工具均旨在支持服务器端或通用 JS 代码。它们的环境并不包括您期望在浏览器中使用的 API(例如创建和使用 DOM 和 HTML 元素),也未包含任何视觉组件或渲染目标概念(即,不仅仅是元素,而是通过 CSS 将这些元素可视化到视口)。

因此,如果您尝试渲染 React 元素以便对其进行测试等操作,这些通用运行时将会失败,因为没有可用的 documentwindow 对象。

另一方面,如果您在浏览器中运行测试,那么如果没有 polyfill 或一些额外的工作,您可能会无法使用这些运行时中的内置 API。一个常见的问题是读取和写入文件:无法在浏览器中执行 import { fs } from 'node:'fs'; 操作,然后在测试中以这种方式读取文件。

这个“Web”与“后端”API 问题有点超出了测试范围,因为同时拥有服务器和客户端部分的代码库可能很尴尬,但它与编写可测试代码的思路相关联,我们将在本课程中再次探讨此问题。

测试算法或业务逻辑

您的部分代码不需要 Node 或浏览器导入即可运行,因此无需进行测试。本课程稍后会介绍这一点,但在构建代码库时,应使其纯粹的业务逻辑与渲染分离,或者使用节点专用代码更易于测试。

举个例子,您可能有一个 Node 函数,用于从磁盘读取和写入文件,并在此过程中对文件进行修改。通过重构函数以接受执行磁盘读写的函数,您可以使其可在任何位置测试。

在这种情况下,您可以使用任何环境在服务器端运行时或浏览器中测试此代码。在测试中,您可以提供用于在内存中存储虚拟文件或返回占位符数据的帮助程序。这种辅助程序可以在测试中引入,因为检查 fs.writeFileSync 是否正常运行并不重要。请重点关注您的代码以及使其独一无二或存在风险的原因。

模拟浏览器 API

许多测试框架(例如 Vitest)为您提供了一个选项,让您可以在不运行浏览器的情况下模拟浏览器的 API 环境。Vitest 在内部使用一个名为 JSDOM 的库。对于使用浏览器开销很高的简单组件测试,该方法非常有用。

任何模拟库的一个常见特性是,虽然它们可以模拟浏览器(例如 DOM、元素和 Cookie),但它们并不包含可视组件。这意味着它们提供了使用 HTML 元素和其他基元的命令式方法,但无法将输出呈现到图片或屏幕上,也无法检查元素在网页上的位置(以像素为单位)。

同样,此选择非常适合组件测试,在这种测试中,组件表示 React 元素、Web 组件等。这些类型的组件通常以相对较小的方式创建 DOM 并与之交互,而模拟浏览器可以提供足够的功能来确认组件以您预期的方式运行。下一部分包含使用 Vitest 和 JSDOM 的 React 组件测试示例。

模拟浏览器是一种成熟的做法(JSDOM 已于 2014 年发布),但它始终与使用真实浏览器不同。这些差异很明显:例如,JSDOM 不包含布局引擎,因此无法检查元素的大小或测试复杂手势(如滑动)。这些差异也可能很细微并且不明确,因此最好确保基于 JSDOM 的测试简洁明了,这样您就可以对任何行为偏离真实情况的风险进行“计时”。

控制真正的浏览器

要以用户的真实体验来测试您的代码,最好使用真实浏览器。实际上,测试支持浏览器的运行时将启动并控制真实浏览器的实例,即使它们在 Node.js 内“启动”也是如此。

在测试过程中控制浏览器意味着它会像用户一样打开,这样您的测试就可以通过加载网址、自定义 HTML 和 JS 或执行测试所需的任何其他内容来控制浏览器。然后,您可以编写代码以充当用户,例如通过控制鼠标或在输入框中输入输入。

WebdriverIOWeb Test Runner 等现代工具可以控制所有主流浏览器,甚至可以同时运行多个实例。这些浏览器可以在测试运行程序旁边运行(例如,在您自己的计算机上运行,或作为 CI 操作的一部分运行),也可以外包给将为您运行这些浏览器的外部商业服务。

较为成熟的测试库(包括 Vitest 和 Jest)通常具有浏览器模式,但由于其来源来自 Node.js,它们的浏览器模式通常“固定”且缺少实用功能。例如,Vitest 无法在浏览器中模拟模块导入,这是我们将在下一页的示例中使用的强大基元。

实际应用

随着测试变得越来越复杂,使用真实的浏览器变得越来越重要。

  • 对于不使用 DOM 功能或只使用极少功能(即使是在 Node.js 和类似运行时中可用的功能(如 fetchEventTarget)中的功能)的测试,环境无关紧要。
  • JSDOM 适用于小型组件测试。
  • 对于大型测试(例如可以模拟用户登录并执行核心操作的端到端测试),可以完全在真实浏览器中运行。

本部分包含大量理论知识,介绍了有关在何处运行测试的不同观点。在实践中,代码库通常会根据您的需求和测试工具提供的功能,使用许多不同的方法进行不同类型的测试。

检查您的掌握程度

模拟层 jsdom *不支持* 哪些浏览器功能?

布局引擎。
由于 JSDOM 并不是一种可视化工具,因此它无法用于检查元素在网页上的位置、其已解析的 CSS 属性或网站布局的任何其他部分。
WebSocket
JSDOM 包含 WebSocket polyfill,因此使用它的代码可以正常运行。
requestAnimationFrame
使用 `pretendToBeVisual` 标志时,jsdom 会以 60fps 的速度调用“ animation”回调,即使实际未绘制任何内容也是如此。