由于 Rspack 使用 Rust + TypeScript 代码混合编写,因此会针对两者使用不同的测试方案。
Rust 测试仅适用于原子功能的单元测试,无法测试完整构建流程。如需测试完整构建流程,请编写 Node 测试用例
通过 ./x test rust 或者 cargo test 可运行 Rust 代码中的测试用例。
用例编写在 Rust 代码内部,用于对当前文件内的函数提供单元测试。如:
fn add(a: u8, b: u8) -> u8 {
a + b
}
#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
}更多信息请参考:Rust: How to Write Tests
Rspack 的测试用例包括如下:
tests/rspack-test 文件夹下,会通过模拟构建流程以运行测试用例。通常情况下,都在此文件夹下添加测试用例。packages/{name}/tests 文件夹下,仅当修改对应包时添加/修改。通过在根文件夹下运行 ./x test unit 或 pnpm run test:unit 即可运行 Rspack 测试。
也可以进入 tests/rspack-test 文件夹并运行 npm run test 来运行测试用例,并且对测试流程进行更精细的控制:
-u 参数,如 npm run test -- -u-t 参数,如 npm run test -- -t configCases/asset 即可仅运行 tests/rspack-test/configCases/asset 文件夹下的用例。匹配支持正则,详见 rstest可以通过如下方式运行这些测试用例:
./x test unit 或 pnpm run test:unit。tests/rspack-test 目录下运行 npm run test。tests/rspack-test 目录下运行 npm run test -- -u。tests/rspack-test 目录下运行 npm run test -- {args}。tests/rspack-test 目录下运行 npm run test -- -t path-of-spec。
npm run test -- -t configCases/asset 即可仅运行 tests/rspack-test/configCases/asset 文件夹下的用例(config 会自动映射到 configCases,其他文件夹类似)。NAPI_RS_FORCE_WASI=1: 强制使用 Rspack Wasm 而不是原生绑定WASM=1:启用 Wasm 专用的测试配置NODE_NO_WARNINGS=1: 关闭 Node.js 的 Wasm 警告。NAPI_RS_FORCE_WASI=1 WASM=1 NODE_NO_WARNINGS=1 pnpm run test:unit文件夹 tests/rspack-test 的结构如下所示:
.
├── js #用于存放构建生成的产物和临时文件
├── __snapshots__ #用于存放测试快照
├── {Name}.test.js #常规测试的入口
├── {Name}.hottest.js #热更新流程测试入口
├── {Name}.difftest.js #产物对比测试入口
├── {name}Cases #测试用例存放目录
└── fixtures #通用测试文件{Name}.test.js 作为测试的入口文件,会遍历 {name}Cases 并运行其中的用例。因此当你需要添加/修改测试用例时,请根据需要测试的功能类型在相应的 {name}Cases 文件夹下添加用例。
目前已有的测试类型有:
rspack.config.js 时可使用此测试类型。rspack.config.js 添加特定配置才可以运行时,且不符合其他场景时可使用此测试类型。target=async-node,HotWeb 会固定使用 target=web,HotWorker 会固定使用 target=webworker。compilation.errors 和 compilation.warnings 的互操作。请优先在以上测试类型中添加用例。
| 测试入口 | tests/Normal.test.js |
|---|---|
| 用例目录 | tests/normalCases |
| 产物目录 | tests/js/normal |
| 默认配置 | NormalProcessor |
| 产物运行 | 是 |
用例的编写与常规的 rspack 项目相同,但不包含 rspack.config.js 文件,会使用固定的配置构建。
| 测试入口 | tests/Config.test.js |
|---|---|
| 用例目录 | tests/configCases |
| 产物目录 | tests/js/config |
| 默认配置 | ConfigProcessor |
| 产物运行 | 是 |
此测试用例与常规的 rspack 项目相同,可通过添加 rspack.config.js 来指定构建配置,并可通过添加 test.config.js 来控制测试运行时的各种行为,其结构如下:
type TConfigCaseConfig = {
noTests?: boolean; // 不运行测试产物并结束测试
beforeExecute?: () => void; // 运行产物前回调
afterExecute?: () => void; // 运行产物后回调
moduleScope?: (ms: IModuleScope) => IModuleScope; // 运行产物时的模块上下文变量
findBundle?: (
// 运行产物时的产物获取函数,可以更细粒度的控制产物
index: number, // muli compiler 场景时的 compiler 序号
options: TCompilerOptions<T>, // 构建配置对象
) => string | string[];
bundlePath?: string[]; // 运行产物时的产物文件名称(优先级低于 findBundle)
nonEsmThis?: (p: string | string[]) => Object; // CJS 产物运行时的 this 对象,若不指定则默认为当前模块的 module.exports
modules?: Record<string, Object>; // 运行产物时预先添加的模块,require 时会优先从此处读取
timeout?: number; // 用例的超时时间
};
/** @type {import("../../../..").TConfigCaseConfig} */
module.exports = {
// ...
};| 测试入口 | Hot{Target}.test.js |
|---|---|
| 用例目录 | tests/hotCases |
| 产物目录 | tests/js/hot-{target} |
| 默认配置 | HotProcessor |
| 产物运行 | 是 |
此测试用例与常规的 rspack 项目相同,可通过添加 rspack.config.js 来指定构建配置
对应的在变更的文件内通过 --- 分割变更前后的代码:
module.exports = 1; // 初始构建
---
module.exports = 2; // 首次热更新
---
module.exports = 3; // 第二次热更新在用例的代码中,通过 NEXT_HMR 方法控制文件变更时机,并在其中添加测试代码:
import value from './file';
it('should hot update', done => {
expect(value).toBe(1);
// 使用 tests/rspack-test/hotCases/update.js 触发更新
await NEXT_HMR();
expect(value).toBe(2);
await NEXT_HMR();
expect(value).toBe(3);
});
module.hot.accept('./file');| 测试入口 | HotSnapshot.hottest.js |
|---|---|
| 用例目录 | tests/hotCases |
| 产物目录 | tests/js/hot-snapshot |
| 默认配置 | 与 Hot 相同 |
| 产物运行 | 是 |
与 Hot{Target} 测试使用相同的测试用例。并在用例文件夹下生成 __snapshots__/{target}/{step}.snap.txt 文件,用于对每一次 HMR 的增量产物进行 snapshot 测试。
Snapshot 结构如下:
hot-update.json 元数据文件内容,其中
"c":本次 HMR 需要更新的 chunk 的 id"r":本次 HMR 需要移除的 chunk 的 id"m":本次 HMR 需要移除的 module 的 idhot-update.js 补丁文件信息,其中:
| 入口文件 | Watch.test.js |
|---|---|
| 用例目录 | tests/watchCases |
| 产物目录 | tests/js/watch |
| 默认配置 | WatchProcessor |
| 产物运行 | 是 |
由于 Watch 构建需要分多步进行,可通过添加 rspack.config.js 来指定构建配置。其用例的目录结构较为特殊,会以自增的数字表示变更批次:
.
├── 0 # WATCH_STEP=0,用例初始代码
├── 1 # WATCH_STEP=1,第一次变更的差异文件
├── 2 # WATCH_STEP=2,第二次变更的差异文件
└── rspack.config.js同时在测试的代码中,可以通过 WATCH_STEP 变量获取当前的变更批次数字。
| 测试入口 | StatsOutput.test.js |
|---|---|
| 用例目录 | tests/statsOutputCases |
| 产物目录 | tests/js/statsOutput |
| 默认配置 | StatsProcessor |
| 产物运行 | 否 |
用例的编写与常规的 rspack 项目相同,运行后会将控制台输出信息生成快照并存放在 tests/rspack-test/__snapshots__/StatsOutput.test.js.snap 中。
由于部分 StatsOutput 测试用例包含 hash。因此当你修改了产物代码时,请通过 -u 参数刷新这些用例的快照。
| 入口文件 | StatsAPI.test.js |
|---|---|
| 用例目录 | tests/statsAPICases |
| 产物目录 | 无 |
| 默认配置 | 无 |
| 产物运行 | 否 |
此测试固定使用 tests/rspack-test/fixtures 作为构建的源码,因此用例以单文件编写,其输出结构如下:
type TStatsAPICaseConfig = {
description: string, // 用例描述
options?: (context: ITestContext) => TCompilerOptions<T>, // 用例构建配置
build?: (context: ITestContext, compiler: TCompiler<T>) => Promise<void>, // 用例构建方式
check?: (stats: TCompilerStats<T>, compiler: TCompiler<T>) => Promise<void>, // 用例的 stats 检测函数
};
/** @type {import('../..').TStatsAPICaseConfig} */
module.exports = {
// ...
};| 入口文件 | Diagnostics.test.js |
|---|---|
| 用例目录 | tests/diagnosticsCases |
| 产物目录 | tests/js/diagnostics |
| 默认配置 | DiagnosticProcessor |
| 产物运行 | 否 |
此测试用例与常规的 rspack 项目相同,可通过添加 rspack.config.js 来指定构建配置,但额外会在用例目录下添加 stats.err 文件用于存储警告/错误的快照,如需刷新请使用 -u 参数。
| 入口文件 | Hash.test.js |
|---|---|
| 用例目录 | tests/hashCases |
| 产物目录 | 无 |
| 默认配置 | HashProcessor |
| 产物运行 | 否 |
此测试用例与常规的 rspack 项目相同,但额外会在用例目录下添加 test.config.js 文件,并指定 validate() 方法用于在构建结束后检测 stats 对象中的 hash 信息:
type THashCaseConfig = {
validate?: (stats: TCompilerStats<T>) => void,
};
/** @type {import('../..').THashCaseConfig} */
module.exports = {
// ...
};| 入口文件 | Compiler.test.js |
|---|---|
| 用例目录 | tests/compilerCases |
| 产物目录 | 无 |
| 默认配置 | 无 |
| 产物运行 | 否 |
此测试固定使用 tests/rspack-test/fixtures 作为构建的源码,因此用例以单文件编写,其输出结构如下:
interface TCompilerCaseConfig {
description: string; // 用例描述
options?: (context: ITestContext) => TCompilerOptions<T>; // 用例构建配置
compiler?: (context: ITestContext, compiler: TCompiler<T>) => Promise<void>; // 用例 compiler 创建方式
build?: (context: ITestContext, compiler: TCompiler<T>) => Promise<void>; // 用例构建方式
check?: (
context: ITestContext,
compiler: TCompiler<T>,
stats: TCompilerStats<T>,
) => Promise<void>; // 用例的检测函数
}
/** @type {import('@rspack/core').TCompilerCaseConfig} */
module.exports = {
// ...
};| 入口文件 | Defaults.test.js |
|---|---|
| 用例目录 | tests/defaultCases |
| 产物目录 | 无 |
| 默认配置 | 无 |
| 产物运行 | 否 |
此测试不会执行真实的构建,仅会生成构建配置并观察与默认配置的差异。基础的默认配置会生成快照并存放在 tests/rspack-test/__snapshots__/Defaults.test.js.snap 中。
此测试固定使用 tests/rspack-test/fixtures 作为构建的源码,因此用例以单文件编写,其输出结构如下:
interface TDefaultsCaseConfig {
description: string; // 用例描述
cwd?: string; // 用例的生成构建配置时的 process.cwd,默认为 `tests/rspack-test` 目录
options?: (context: ITestContext) => TCompilerOptions<ECompilerType.Rspack>; // 用例构建配置
diff: (
diff: Assertion<Diff>,
defaults: Assertion<TCompilerOptions<ECompilerType.Rspack>>,
) => Promise<void>; // 与默认配置之间的差异
}
/** @type {import('../..').TDefaultsCaseConfig} */
module.exports = {
// ...
};| 入口文件 | Error.test.js |
|---|---|
| 用例目录 | tests/errorCases |
| 产物目录 | 无 |
| 默认配置 | ErrorProcessor |
| 产物运行 | 否 |
此测试的用例固定使用 tests/rspack-test/fixtures 作为构建的源码,因此测试用例以特定的配置结构编写:
interface TErrorCaseConfig {
description: string; // 用例描述
options?: (
options: TCompilerOptions<T>,
context: ITestContext,
) => TCompilerOptions<T>; // 用例配置
build?: (context: ITestContext, compiler: TCompiler<T>) => Promise<void>; // 用例构建方式
check?: (stats: TStatsDiagnostics) => Promise<void>; // 用例的检测函数
}
/** @type {import('../..').TErrorCaseConfig} */
module.exports = {
// ...
};| 入口文件 | Hook.test.js |
|---|---|
| 用例目录 | tests/hookCases |
| 产物目录 | 无 |
| 默认配置 | HookProcessor |
| 产物运行 | 否 |
会记录 hook 的出入参并存放在快照 hooks.snap.txt 中,最终产物代码的快照存放在 output.snap.txt 中。
此测试的用例固定使用 tests/rspack-test/fixtures 作为构建的源码,因此测试用例以特定的配置结构编写:
interface THookCaseConfig {
description: string; // 用例描述
options?: (
options: TCompilerOptions<T>,
context: ITestContext,
) => TCompilerOptions<T>; // 用例配置
compiler?: (context: ITestContext, compiler: TCompiler<T>) => Promise<void>; // 创建 compiler 实例后回调
check?: (context: ITestContext) => Promise<void>; // 构建完成后回调
}
/** @type {import("@rspack/test-tools").THookCaseConfig} */
module.exports = {
// ...
};| 入口文件 | TreeShaking.test.js |
|---|---|
| 用例目录 | tests/treeShakingCases |
| 产物目录 | tests/js/treeShaking |
| 默认配置 | TreeShakingProcessor |
| 产物运行 | 否 |
此测试用例与常规的 rspack 项目相同,可通过添加 rspack.config.js 来指定构建配置,但会将最终产物生成快照并存放在 __snapshots__/treeshaking.snap.txt 中。
| 入口文件 | Builtin.test.js |
|---|---|
| 用例目录 | tests/builtinCases |
| 产物目录 | tests/js/builtin |
| 默认配置 | BuiltinProcessor |
| 产物运行 | 否 |
此测试用例与常规的 rspack 项目相同,可通过添加 rspack.config.js 来指定构建配置。
但根据目录的不同,会将不同的产物生成快照并存放在 __snapshots__/output.snap.txt 中:
.css 后缀文件生成快照.css 和 .js 后缀文件生成快照.html 后缀文件生成快照.js 后缀文件生成快照