Snowpack 项目接入 @web/test-runner 测试框架完整指南
【免费下载链接】snowpackESM-powered frontend build tool. Instant, lightweight, unbundled development. ✌️项目地址: https://gitcode.com/gh_mirrors/sn/snowpack
本指南以 Snowpack 官方推荐的浏览器端测试运行器 @web/test-runner(WTR)为主题,基于 docs/guides/web-test-runner.md 的完整设置流程,结合仓库中的@snowpack/web-test-runner-plugin源码与各官方模板配置,讲解从安装、配置到运行测试的完整链路,并深入剖析该方案"复用 Snowpack 构建管线、无需第二套构建配置"的底层原理。读完本文,你将能在自己的 Snowpack 项目中零成本接入 WTR,并理解其与 Snowpack 深度集成的实现机制。
为什么 Snowpack 官方推荐 @web/test-runner
在 Snowpack 生态中,测试运行器的选择经历了一次明确的演进。从 Snowpack Testing Guide 可以看到,Snowpack 支持 Mocha、Jest、Jasmine、AVA、Cypress 等主流测试框架——只要集成方式正确——但官方目前推荐的是 @web/test-runner(WTR),并给出了三个核心理由:
- 性能更优:官方基准测试显示 WTR 比此前的推荐方案 Jest 更快;
- 环境更贴近生产:WTR 直接在真实浏览器(而非 Node.js 模拟 DOM)中运行测试,测试环境与生产环境的一致性更高;
- 复用 Snowpack 构建管线:这是最关键的一点——WTR 直接运行你为项目配置好的那套 Snowpack 构建流程,测试无需第二套构建配置,从而既提升了测试置信度,又省去了项目里成百上千个多余的构建依赖包。
其中第三点正是本仓库中@snowpack/web-test-runner-plugin存在的意义,也是本文后续源码剖析部分的核心。
准备工作:认识两个依赖
在开始安装前,先厘清本指南涉及的两个包各自扮演的角色:
- @web/test-runner:由 Modern Web 社区维护的浏览器端测试运行器,负责在真实浏览器中启动测试页、执行测试用例并汇总结果,是测试的"执行引擎";
- @snowpack/web-test-runner-plugin:Snowpack 官方提供的 WTR 插件(本仓库中位于 plugins/web-test-runner-plugin/),负责在 WTR 内部拉起一个按测试模式配置的 Snowpack 开发服务器,让测试文件走与日常开发完全相同的构建管线。
两者的分工可以概括为:WTR 决定"怎么跑测试",Snowpack 插件决定"测试文件怎么被构建"。
完整接入步骤
本指南以 React 项目为例,最终效果等价于官方模板 app-template-react 中现成的测试配置。若你使用其他框架,仅需替换 React 特有的步骤。
第 1 步:安装依赖
在项目根目录执行(先别急着按回车,后面还有依赖要一起装):
npm install --save-dev @web/test-runner @snowpack/web-test-runner-plugin chai- chai:WTR 默认不内置断言库,官方示例使用 chai 的
expect风格断言; - Testing Library:如果使用 React、Vue、Svelte 或 Preact,请补充对应的 Testing Library 包。以 React 为例安装
@testing-library/react(对应 Svelte 则为@testing-library/svelte,见 app-template-svelte); - TypeScript 用户:额外安装
@types/mocha和@types/chai,用于提供describe/it与expect的类型声明。
以官方 React 模板 package.json 为参考,其 devDependencies 中与本测试链路相关的依赖为:
"devDependencies": { "@snowpack/web-test-runner-plugin": "^0.2.2", "@testing-library/react": "^11.2.6", "@web/test-runner": "^0.13.3", "chai": "^4.3.4", "snowpack": "^3.8.0" }第 2 步:创建 web-test-runner.config.js
在项目根目录新建web-test-runner.config.js:
process.env.NODE_ENV = 'test'; module.exports = { plugins: [require('@snowpack/web-test-runner-plugin')()], };文件开头设置process.env.NODE_ENV = 'test'的目的,是让构建管线在测试模式下运行(部分库会依据该变量切换行为)。这段配置与官方模板 app-template-react/web-test-runner.config.js 完全一致,React、Svelte、Preact 等模板均采用相同写法。
⚠️重要注意事项(官方文档特别强调):不要把@snowpack/web-test-runner-plugin加进snowpack.config.mjs的plugins数组中!它只应存在于web-test-runner.config.js。如果需要为测试指定 Snowpack 侧的选项,请使用testOptions配置项(详见下文"testOptions 配置"一节)。
第 3 步:添加 test 脚本
在项目package.json的scripts中添加测试命令:
"scripts": { "start": "snowpack dev", "build": "snowpack build", + "test": "web-test-runner \"src/**/*.test.jsx\"", ... },- 若测试文件扩展名不同,将
.jsx替换为实际的测试文件类型; - 需要匹配多种类型时,用花括号加逗号并列。例如同时匹配
.jsx、.js和.ts文件:
"test": "web-test-runner \"src/**/*.test.{jsx,js,ts}\""💡 提示:
wtr可作为web-test-runner的简写,即"test": "wtr \"src/**/*.test.jsx\""。
第 4 步:编写一个测试用例
以官方 React 模板的 App.test.jsx 为例,一个典型的 WTR + Testing Library + chai 测试长这样:
import * as React from 'react'; import { render } from '@testing-library/react'; import { expect } from 'chai'; import App from './App'; describe('<App>', () => { it('renders learn react link', () => { const { getByText } = render(<App />); const linkElement = getByText(/learn react/i); expect(document.body.contains(linkElement)); }); });Svelte 模板的 App.test.js 展示了同样结构在其他框架下的写法(使用@testing-library/svelte)。运行npm test即可在真实浏览器中执行这些用例。
testOptions 配置:在 Snowpack 侧管理测试文件
官方文档指出,如果需要指定测试相关选项,应使用testOptions。从 snowpack/src/config.ts 的默认配置可以看到其默认值为:
testOptions: { files: ['__tests__/**/*', '**/*.@(spec|test).*'], },即默认情况下,Snowpack 会把__tests__目录下的所有文件、以及任意目录下命名为*.spec.*或*.test.*的文件都视为测试文件。该配置在 snowpack/src/types.ts 中被类型化为files: string[](glob 数组)。
从源码实现看,testOptions.files有实际的功能影响,而不仅是文档说明:
- 在开发服务器中,snowpack/src/commands/dev.ts 会将测试文件加入排除列表(
excludeGlobs),避免测试文件被当作应用入口被监听构建——唯一的例外是当mode === 'test'时(这正是插件加载配置时使用的模式); - 在生产构建流程 snowpack/src/build/process.ts 中,测试文件同样被排除在最终构建产物之外;
- 在依赖扫描阶段 snowpack/src/scan-imports.ts,默认情况下测试文件不会被扫描依赖,仅在显式包含测试时才会纳入。
这些实现共同保证了:测试文件只服务于测试场景,不会污染日常开发与生产构建。
深入原理:@snowpack/web-test-runner-plugin 如何复用 Snowpack 构建管线
官方推荐 WTR 的核心卖点是"复用同一套 Snowpack 构建管线",其实现全部浓缩在 plugins/web-test-runner-plugin/plugin.js 这个约 60 行的文件中。下面逐段拆解其工作方式。
插件生命周期:serverStart / serverStop
WTR 服务器启动时,插件通过serverStart钩子完成 Snowpack 侧的初始化(plugin.js):
async serverStart({fileWatcher}) { config = await snowpack.loadConfiguration({ mode: 'test', packageOptions: {external: ['/__web-dev-server__web-socket.js']}, devOptions: {open: 'none', output: 'stream', hmr: false}, }); console.log('[snowpack] starting server...'); fileWatcher.add(Object.keys(config.mount)); server = await snowpack.startServer({config, lockfile: null}); }关键点解读:
mode: 'test':以测试模式加载项目的snowpack.config.mjs,使构建行为符合测试场景(如不排除测试文件、不启用 HMR);packageOptions.external:将 WTR 内部使用的 WebSocket 通信脚本排除在依赖打包之外,避免与 WTR 自身的客户端脚本冲突;devOptions.open: 'none'/hmr: false:测试场景下不自动打开浏览器、不启用热更新;fileWatcher.add(Object.keys(config.mount)):把 Snowpack 配置中挂载(mount)的目录注册到 WTR 的文件监听器,测试运行期间文件变更同样会触发 Snowpack 重建。
serverStop钩子则调用server.shutdown()关闭 Snowpack 服务器,保证进程干净退出(plugin.js)。
请求分发:serve 钩子
当浏览器向测试服务器发起资源请求时,serve钩子拦截请求并把它们转交给 Snowpack 处理(plugin.js):
async serve({request}) { if (isTestRunnerFile(request.url)) { return; } const reqPath = request.path; try { const result = await server.loadUrl(reqPath, {isSSR: false}); return {body: result.contents, type: result.contentType}; } catch { return; } }- 以
/__web-dev-server或/__web-test-runner开头的 URL 属于 WTR 自身提供的虚拟文件,直接放行不交给 Snowpack(见isTestRunnerFile判断,plugin.js); - 其余所有资源请求都调用 Snowpack 服务器的
loadUrl,让测试页面中的每个模块都经过你已配置好的转换管线(ESM 转换、JSX/TS 编译、CSS 处理、插件链等)。
这意味着:你在 Snowpack 里配置的 alias、插件、mount 等规则,在测试环境下全部生效,测试验证的正是生产代码的实际构建结果。
导入重写:transformImport 钩子
现代 ESM 下,测试文件中的相对导入会被 WTR 直接解析,但对裸模块导入(如import App from './App'),transformImport钩子负责把它们转换成 Snowpack 产出的最终 URL(plugin.js):
transformImport({source}) { if (!isTestFilePath(source) || isTestRunnerFile(source)) { return; } const reqPath = source.substring(0, source.indexOf('?') === -1 ? undefined : source.indexOf('?')); const sourcePath = path.join(config.root || process.cwd(), reqPath); try { return snowpack.getUrlForFile(sourcePath, config); } catch { return; } }实现逻辑为:对每个非测试框架自身的导入源,剥去查询字符串(?之后的部分),拼出磁盘上的真实文件路径,再调用snowpack.getUrlForFile获取其在 Snowpack 中的最终 URL。这样测试文件里的import App from './App'在测试运行时会被解析到与snowpack dev完全一致的 URL,彻底消除开发与测试之间的解析差异。
各框架官方模板速查
本仓库的 Create Snowpack App 模板中,已预置 WTR 测试配置的包括:
| 模板 | test 脚本 | 测试文件示例 |
|---|---|---|
| app-template-react | web-test-runner "src/**/*.test.jsx" | App.test.jsx |
| app-template-react-typescript | web-test-runner "src/**/*.test.tsx" | App.test.tsx |
| app-template-preact | web-test-runner "src/**/*.test.jsx" | App.test.jsx |
| app-template-preact-typescript | web-test-runner "src/**/*.test.tsx" | App.test.tsx |
| app-template-svelte | web-test-runner "src/**/*.test.js" | App.test.js |
| app-template-svelte-typescript | web-test-runner "src/**/*.test.ts" | App.test.ts |
所有模板的 web-test-runner.config.js 内容完全一致,差异仅在package.json的 test 脚本与测试文件的扩展名。你可以直接以对应模板为起点快速搭建项目,或对照模板校验自己的配置。
常见问题与注意事项
Q:可以把插件写进 snowpack.config.mjs 吗?不可以。插件必须在web-test-runner.config.js中注册(参见 web-test-runner-plugin 的 README),它没有可配置选项(Options: None),所有 Snowpack 侧的调整都应落在项目的snowpack.config.mjs中,通过testOptions等配置项完成。
Q:为什么测试文件不会出现在生产构建里?如"testOptions 配置"一节所述,testOptions.files的默认 glob 会同时作用于构建与依赖扫描流程(snowpack/src/build/process.ts),保证测试文件只服务于测试场景。
Q:是否需要为测试单独安装一堆构建依赖?不需要。这正是官方推荐 WTR 的核心优势之一:测试复用snowpack dev/snowpack build同一套构建管线,无需像 Jest 那样额外配置 jsdom、transform 等一整套测试专用构建链(Jest 场景可参考 docs/guides/jest.md,它需要额外维护独立的 jest 配置与jest.setup.js)。
Q:如何让测试文件同时支持多种扩展名?在 test 脚本中使用花括号语法,例如web-test-runner "src/**/*.test.{jsx,js,ts}"。
Q:WTR 默认没有断言库,怎么办?WTR 本身不捆绑断言库,官方模板使用chai(import {expect} from 'chai'),也可按需替换为其他浏览器端可用的断言库。
小结
在 Snowpack 项目中接入 @web/test-runner 只需三步:安装@web/test-runner、@snowpack/web-test-runner-plugin与chai(以及对应框架的 Testing Library);创建只注册 Snowpack 插件的web-test-runner.config.js;在package.json中添加test脚本。其背后的架构价值在于:WTR 与 Snowpack 通过serve/transformImport两个钩子深度协作,让测试代码与日常开发共享完全相同的构建管线、依赖解析与插件链,从而以更少的配置、更贴近生产的环境获得更可信的测试结果。相关源码与模板均可在本仓库的 plugins/web-test-runner-plugin/plugin.js 与 create-snowpack-app 各模板中直接查阅。
【免费下载链接】snowpackESM-powered frontend build tool. Instant, lightweight, unbundled development. ✌️项目地址: https://gitcode.com/gh_mirrors/sn/snowpack
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考