测试环境

正如“什么是测试”中所介绍的,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 的代码分离可以使其更易于测试。

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

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

模拟浏览器 API

许多测试框架(如 Vitest)为您提供了一个选项,用于模拟浏览器的 API 环境,而无需运行浏览器。Vitest 内部使用一个名为 JSDOM 的库。对于使用浏览器开销较高的简单组件测试,这可能是一个不错的选择。

任何模拟库的一个共同特点是,尽管它们可以模拟浏览器(例如,DOM、元素和 Cookie),但它们没有视觉组件。这意味着它们将提供一种命令式的方式来处理 HTML 元素和其他基元,但您无法将输出渲染到图像或屏幕,也无法检查元素在页面上的像素位置。

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

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

控制真实浏览器

要像用户体验一样测试您的代码,使用真实浏览器是最佳选择。在实践中,支持浏览器的测试运行时将启动并控制真实浏览器的实例,即使它们在 Node.js 内部运行“启动”也是如此。

将浏览器作为测试的一部分进行控制意味着它将像用户一样打开,从而允许您的测试通过加载 URL、自定义 HTML 和 JS 或执行测试所需的任何内容来控制它。然后,您可以编写代码来充当用户,例如通过控制鼠标或在输入框中键入输入。

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

更多成熟的测试库(包括 Vitest 和 Jest)通常具有浏览器模式,但由于它们起源于 Node.js,因此它们的浏览器模式通常是“附加”的,并且缺少有用的功能。例如,Vitest 无法在浏览器中模拟模块导入,这是我们在下一页的示例中使用的强大基元。

实践中

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

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

本节侧重于理论,并介绍了关于在何处运行测试的不同观点。在实践中,您的代码库通常会根据您的需求和测试工具提供的功能,对不同类型的测试使用许多不同的方法。

检查您的理解程度

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

requestAnimationFrame
布局引擎。
WebSocket