mock-fs API参考手册:mock、load、bypass、getMockRoot等8个接口速查清单
mock-fs API参考手册:mock、load、bypass、getMockRoot等8个接口速查清单
mock-fs 是一个 Node.js 的内存文件系统模拟库,它可以临时接管内置的 fs 模块,让你在单元测试中用“假文件”替代真实磁盘文件,测试更快、更干净,也不再需要一大堆测试夹具。本文将速查 mock-fs 的 8 个核心接口:mock、load、bypass、restore、getMockRoot、file、directory、symlink,帮你一次看懂 API 全貌。
一、8个接口速查总览
先上一张总览表,方便按图索骥:
| # | 接口 | 作用 | 一句话记忆 |
|---|---|---|---|
| 1 | mock(config, options) |
启用模拟文件系统 | “开机”:把 fs 指向内存 |
| 2 | mock.load(path, options) |
把真实文件/目录加载进模拟 | “搬砖”:真文件搬到内存 |
| 3 | mock.bypass(fn) |
临时穿透模拟,访问真实磁盘 | “开个后门” |
| 4 | mock.restore() |
恢复真实文件系统 | “关机”:还原 fs |
| 5 | mock.getMockRoot() |
获取模拟文件系统的根目录 | “查目录” |
| 6 | mock.file(properties) |
创建带属性的文件工厂 | 精细造文件 |
| 7 | mock.directory(properties) |
创建带属性的目录工厂 | 精细造目录 |
| 8 | mock.symlink(properties) |
创建符号链接工厂 | 造快捷方式 |
这 8 个接口全部在 lib/index.js 中统一导出,官方完整文档见 readme.md。
二、接口逐个拆解
1. mock(config, options) —— 启用模拟文件系统
这是 mock-fs 的主入口(一个函数本身),源码见 lib/index.js。传入一个配置对象,键是相对路径,值代表文件内容或目录结构:
const mock = require('mock-fs');
mock({
'path/to/fake/dir': {
'some-file.txt': 'file content here',
'empty-dir': {} // 空目录
},
'path/to/some.png': Buffer.from([8, 6, 7, 5, 3, 0, 9])
});
三个约定要记住:
- 字符串 / Buffer 值 → 创建文件
- 对象值 → 创建目录(空对象
{}即空目录) - 路径一律用正斜杠
/,Windows 上也不要换反斜杠
第二个参数 options 控制两个默认目录是否创建:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
createCwd |
boolean | true | 是否为 process.cwd() 创建目录 |
createTmp |
boolean | true | 是否为 os.tmpdir() 创建目录 |
💡 不带参数直接调用 mock(),会得到一个只含 cwd 和 tmpdir 两个目录的空文件系统。
2. mock.load(path, options) —— 加载真实文件到模拟
不想手写测试数据时,可以用 load 把磁盘上的真实文件或目录“搬”进模拟文件系统,实现见 lib/loader.js。
mock({
// 懒加载:读到时才真正读取真实内容
'my-file.txt': mock.load(path.resolve(__dirname, 'assets/file1.txt')),
// 预加载:立即读入内容
'ready.js': mock.load(path.resolve(__dirname, 'scripts/ready.js'), {lazy: false}),
// 递归加载整个目录
'node_modules': mock.load(path.resolve(__dirname, '../node_modules'))
});
两个常用选项:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
lazy |
boolean | true | 懒加载,真正读取文件内容时才加载 |
recursive |
boolean | true | 是否递归加载子目录 |
stat 信息(时间戳、权限等)会被一并复制,所以测试里 fs.stat() 的结果和真实文件一致。
3. mock.bypass(fn) —— 穿透模拟访问真实磁盘
模拟激活期间,所有 fs 调用都打在内存上。偶尔需要读一个“只存在于真实磁盘”的文件,就交给 bypass,实现见 lib/bypass.js:
// 该文件只存在于真实文件系统
const data = mock.bypass(() => fs.readFileSync('/path/file.json', 'utf-8'));
要点:
- 传入一个函数,函数内访问的将是真实文件系统,执行完自动恢复模拟
- 若传入异步函数(返回 Promise),
bypass也会返回 Promise,且会在 Promise 结算后才恢复模拟 - ⚠️ 官方提示:异步用法可能带来意外后果(其他代码在恢复前也访问了文件系统),能不异步就别异步
4. mock.restore() —— 恢复真实文件系统
调用 restore() 即可撤销 mock() 的一切效果,还原 Node 原始的 fs 绑定,源码见 lib/index.js。标准测试姿势:
beforeEach(function() {
mock({ 'fake-file': 'file contents' });
});
afterEach(mock.restore);
📌 特别注意:在 Jest 快照测试等场景下,afterEach 里恢复太迟了,需要在断言前手动调用 mock.restore()。重复调用 restore() 是安全的。
5. mock.getMockRoot() —— 获取模拟根目录
想直接拿到内存文件系统的“根”(一个 Directory 对象)来编程式操作,就用它,源码见 lib/index.js:
const root = mock.getMockRoot();
注意:当前没有启用模拟时,它返回空对象 {},拿到的是空的 Directory。
6. mock.file(properties) —— 精细控制文件属性
字符串只能建一个“普通文件”,而 mock.file() 可以指定权限、属主、时间戳等(工厂实现见 lib/filesystem.js 附近的 file 定义,文件内部结构见 lib/file.js):
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
content |
string | Buffer | - | 文件内容 |
mode |
number | 0666 |
权限位 |
uid / gid |
number | 当前用户/组 | 属主 |
atime / ctime / mtime / birthtime |
Date | new Date() |
各类时间戳 |
mock({
foo: mock.file({
content: 'file content here',
ctime: new Date(1), // 一个“很老”的文件
mtime: new Date(1)
})
});
7. mock.directory(properties) —— 精细控制目录属性
目录版“属性工厂”,实现见 lib/filesystem.js,内部由 lib/directory.js 提供支撑:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mode |
number | 0777 |
权限位 |
uid / gid |
number | 当前用户/组 | 属主 |
items |
Object | - | 目录内容(结构同 config) |
atime / ctime / mtime / birthtime |
Date | new Date() |
各类时间戳 |
mock({
'some/dir': mock.directory({
mode: 0755,
items: {
file1: 'file one content',
file2: Buffer.from([8, 6, 7, 5, 3, 0, 9])
}
})
});
💡 只要默认属性时,直接写对象字面量即可,不必调用 mock.directory()。
8. mock.symlink(properties) —— 创建符号链接
符号链接没有简写,必须走工厂,实现见 lib/filesystem.js,链接本体见 lib/symlink.js:
mock({
'some/dir': {
'regular-file': 'file contents',
'a-symlink': mock.symlink({ path: 'regular-file' })
}
});
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path |
string | 必填 | 指向的源路径 |
mode |
number | 0666 |
权限位 |
uid / gid |
number | 当前用户/组 | 属主 |
atime / ctime / mtime / birthtime |
Date | new Date() |
各类时间戳 |
⚠️ 缺省 path 会直接抛出 Missing "path" property 错误,这是唯一必填项。
三、推荐测试流程:3步组合拳
把接口串起来,标准用法就三步:
beforeEach中mock(config)—— 搭建干净环境- 测试代码随意读写文件 —— 全部落在内存里
afterEach中mock.restore()—— 还原现场
涉及真实资源时再按需加入 mock.load()(搬数据)或 mock.bypass()(穿墙读),形成闭环。
四、相关源码与文档索引
| 内容 | 路径 |
|---|---|
| 主入口与 8 个接口导出 | lib/index.js |
| 文件系统核心(config 解析、工厂) | lib/filesystem.js |
| load 加载逻辑 | lib/loader.js |
| bypass 穿透逻辑 | lib/bypass.js |
| 文件 / 目录 / 符号链接模型 | lib/file.js · lib/directory.js · lib/symlink.js |
| 官方文档(含全部示例) | readme.md |
| 测试用例参考 | test/lib/index.spec.js |
五、安装与注意事项
用 npm 安装(建议作为开发依赖):
npm install mock-fs --save-dev
几个容易踩的坑:
- 引入顺序:先
require('mock-fs'),再引入其他会改动 fs 的库(如graceful-fs),mock 才能生效 - 版本兼容:与
graceful-fs@3.x不兼容,4.x可用 - 权限控制:POSIX 系统上按文件 mode 判定访问权限;Windows 上 mode 不生效
- 运行环境:需要 Node ≥ 12,官方在 Node 18–22 上验证过
掌握以上 8 个接口,mock-fs 的 API 你就已经全部覆盖了——剩下的就是把它写进测试,享受又快又稳的单元测试吧 🚀