@@ -1044,6 +1044,156 @@ test('runs timers as setTime passes ticks', (context) => {
10441044});
10451045```
10461046
1047+ ### File system
1048+
1049+ <!-- YAML
1050+ added: REPLACEME
1051+ -->
1052+
1053+ > Stability: 1.0 - Early development
1054+
1055+ Mocking the file system is a technique commonly used in software testing to
1056+ simulate file operations without actually writing to or reading from the disk.
1057+ This allows for safer, faster, and more predictable tests when working with
1058+ file system operations.
1059+
1060+ Refer to the [ ` MockFileSystem ` ] [ ] class for a full list of methods and features.
1061+
1062+ ** Note:** This feature requires the ` --experimental-test-fs-mocks ` flag.
1063+
1064+ The example below shows how to mock file system operations. Using
1065+ ` .enable({ files: {...} }) ` it will mock the file system methods in the
1066+ [ node: fs ] ( ./fs.md ) and [ node: fs /promises] ( ./fs.md#promises-api ) modules.
1067+
1068+ ``` mjs
1069+ import assert from ' node:assert' ;
1070+ import fs from ' node:fs' ;
1071+ import { test } from ' node:test' ;
1072+
1073+ test (' mocks file system operations' , (context ) => {
1074+ // Enable file system mocking with virtual files
1075+ context .mock .fs .enable ({
1076+ files: {
1077+ ' /virtual/test.txt' : ' Hello, World!' ,
1078+ ' /virtual/data.json' : ' {"key": "value"}' ,
1079+ },
1080+ });
1081+
1082+ // Read virtual files
1083+ const content = fs .readFileSync (' /virtual/test.txt' , ' utf8' );
1084+ assert .strictEqual (content, ' Hello, World!' );
1085+
1086+ // Write to virtual file system
1087+ fs .writeFileSync (' /virtual/new.txt' , ' New content' );
1088+ assert .strictEqual (fs .readFileSync (' /virtual/new.txt' , ' utf8' ), ' New content' );
1089+
1090+ // Check if virtual file exists
1091+ assert .strictEqual (fs .existsSync (' /virtual/test.txt' ), true );
1092+ });
1093+ ```
1094+
1095+ ``` cjs
1096+ const assert = require (' node:assert' );
1097+ const fs = require (' node:fs' );
1098+ const { test } = require (' node:test' );
1099+
1100+ test (' mocks file system operations' , (context ) => {
1101+ // Enable file system mocking with virtual files
1102+ context .mock .fs .enable ({
1103+ files: {
1104+ ' /virtual/test.txt' : ' Hello, World!' ,
1105+ ' /virtual/data.json' : ' {"key": "value"}' ,
1106+ },
1107+ });
1108+
1109+ // Read virtual files
1110+ const content = fs .readFileSync (' /virtual/test.txt' , ' utf8' );
1111+ assert .strictEqual (content, ' Hello, World!' );
1112+
1113+ // Write to virtual file system
1114+ fs .writeFileSync (' /virtual/new.txt' , ' New content' );
1115+ assert .strictEqual (fs .readFileSync (' /virtual/new.txt' , ' utf8' ), ' New content' );
1116+
1117+ // Check if virtual file exists
1118+ assert .strictEqual (fs .existsSync (' /virtual/test.txt' ), true );
1119+ });
1120+ ```
1121+
1122+ By default, the mock file system allows access to both virtual and real files,
1123+ with virtual files taking precedence. When file system mocking is enabled,
1124+ ** all write operations go to the virtual file system** , regardless of whether
1125+ the path exists in the real file system. This prevents tests from accidentally
1126+ modifying the real file system.
1127+
1128+ You can enable isolation mode to completely isolate tests from the real file
1129+ system for read operations as well:
1130+
1131+ ``` mjs
1132+ import assert from ' node:assert' ;
1133+ import fs from ' node:fs' ;
1134+ import { test } from ' node:test' ;
1135+
1136+ test (' complete file system isolation' , (context ) => {
1137+ context .mock .fs .enable ({
1138+ files: {
1139+ ' /virtual/only.txt' : ' Only this file exists' ,
1140+ },
1141+ isolate: true , // Enable full isolation mode
1142+ });
1143+
1144+ // Virtual file works
1145+ assert .strictEqual (fs .readFileSync (' /virtual/only.txt' , ' utf8' ), ' Only this file exists' );
1146+
1147+ // Real files are not accessible
1148+ assert .throws (() => {
1149+ fs .readFileSync (' /etc/passwd' );
1150+ }, {
1151+ code: ' ENOENT' ,
1152+ });
1153+ });
1154+ ```
1155+
1156+ ``` cjs
1157+ const assert = require (' node:assert' );
1158+ const fs = require (' node:fs' );
1159+ const { test } = require (' node:test' );
1160+
1161+ test (' complete file system isolation' , (context ) => {
1162+ context .mock .fs .enable ({
1163+ files: {
1164+ ' /virtual/only.txt' : ' Only this file exists' ,
1165+ },
1166+ isolate: true , // Enable full isolation mode
1167+ });
1168+
1169+ // Virtual file works
1170+ assert .strictEqual (fs .readFileSync (' /virtual/only.txt' , ' utf8' ), ' Only this file exists' );
1171+
1172+ // Real files are not accessible
1173+ assert .throws (() => {
1174+ fs .readFileSync (' /etc/passwd' );
1175+ }, {
1176+ code: ' ENOENT' ,
1177+ });
1178+ });
1179+ ```
1180+
1181+ #### Windows path handling
1182+
1183+ On Windows, use appropriate path separators or forward slashes:
1184+
1185+ ``` js
1186+ test (' Windows path handling' , (t ) => {
1187+ t .mock .fs .enable ({
1188+ files: {
1189+ ' C:/virtual/test.txt' : ' Hello, World!' ,
1190+ // or using backslashes
1191+ // 'C:\\virtual\\test.txt': 'Hello, World!',
1192+ },
1193+ });
1194+ });
1195+ ```
1196+
10471197## Snapshot testing
10481198
10491199<!-- YAML
@@ -3106,6 +3256,190 @@ test('runAll functions following the given order', (context) => {
31063256});
31073257```
31083258
3259+ ## Class: ` MockFileSystem `
3260+
3261+ <!-- YAML
3262+ added: REPLACEME
3263+ -->
3264+
3265+ > Stability: 1.0 - Early development
3266+
3267+ Mocking the file system allows tests to simulate file operations without
3268+ actually reading from or writing to the disk. This makes tests safer, faster,
3269+ and more predictable.
3270+
3271+ The [ ` MockTracker ` ] [ ] provides a top-level ` fs ` export
3272+ which is a ` MockFileSystem ` instance.
3273+
3274+ ** Note:** This class requires the ` --experimental-test-fs-mocks ` flag.
3275+
3276+ ### ` fs.enable([options]) `
3277+
3278+ <!-- YAML
3279+ added: REPLACEME
3280+ -->
3281+
3282+ Enables file system mocking.
3283+
3284+ * ` options ` {Object} Optional configuration options for enabling file system
3285+ mocking. The following properties are supported:
3286+ * ` files ` {Object} An object mapping file paths to their content. Content
3287+ can be a string, ` Buffer ` , or ` Uint8Array ` . Strings are automatically
3288+ converted to ` Buffer ` using UTF-8 encoding. ** Default:** ` {} ` .
3289+ * ` isolate ` {boolean} If ` true ` , only virtual files are accessible and
3290+ any access to paths not in ` files ` will throw ` ENOENT ` . If ` false `
3291+ (the default), virtual files take precedence but real file system
3292+ operations are still allowed for other paths. ** Note:** When mocking is
3293+ enabled, write operations always go to the virtual file system regardless
3294+ of this setting. ** Default:** ` false ` .
3295+ * ` apis ` {Array} An optional array specifying which fs API families to mock.
3296+ Each value mocks the synchronous, callback, and promise versions of that
3297+ API (e.g., ` 'readFile' ` mocks ` fs.readFileSync() ` , ` fs.readFile() ` , and
3298+ ` fsPromises.readFile() ` ). The supported values are ` 'readFile' ` ,
3299+ ` 'writeFile' ` , ` 'appendFile' ` , ` 'stat' ` , ` 'lstat' ` , ` 'access' ` , ` 'exists' ` ,
3300+ ` 'unlink' ` , ` 'mkdir' ` , ` 'rmdir' ` , and ` 'readdir' ` . ** Default:** all
3301+ supported APIs.
3302+
3303+ ** Note:** When file system mocking is enabled, the mock automatically
3304+ creates parent directories for all virtual files.
3305+
3306+ Example usage:
3307+
3308+ ``` mjs
3309+ import { mock } from ' node:test' ;
3310+ import { Buffer } from ' node:buffer' ;
3311+
3312+ mock .fs .enable ({
3313+ files: {
3314+ ' /path/to/file.txt' : ' file content' ,
3315+ ' /path/to/binary.bin' : Buffer .from ([0x00 , 0x01 , 0x02 ]),
3316+ },
3317+ });
3318+ ```
3319+
3320+ ``` cjs
3321+ const { mock } = require (' node:test' );
3322+
3323+ mock .fs .enable ({
3324+ files: {
3325+ ' /path/to/file.txt' : ' file content' ,
3326+ ' /path/to/binary.bin' : Buffer .from ([0x00 , 0x01 , 0x02 ]),
3327+ },
3328+ });
3329+ ```
3330+
3331+ ### ` fs.reset() `
3332+
3333+ <!-- YAML
3334+ added: REPLACEME
3335+ -->
3336+
3337+ Restores the original file system functions and clears all virtual files.
3338+ This function is automatically called when a test using the mock file system
3339+ completes.
3340+
3341+ ### Supported ` fs ` methods
3342+
3343+ The following methods are intercepted by the mock file system:
3344+
3345+ ** Synchronous methods:**
3346+
3347+ * ` fs.readFileSync() `
3348+ * ` fs.writeFileSync() `
3349+ * ` fs.appendFileSync() `
3350+ * ` fs.statSync() `
3351+ * ` fs.lstatSync() `
3352+ * ` fs.existsSync() `
3353+ * ` fs.accessSync() `
3354+ * ` fs.unlinkSync() `
3355+ * ` fs.mkdirSync() `
3356+ * ` fs.rmdirSync() `
3357+ * ` fs.readdirSync() `
3358+
3359+ ** Callback methods:**
3360+
3361+ * ` fs.readFile() `
3362+ * ` fs.writeFile() `
3363+ * ` fs.appendFile() `
3364+ * ` fs.stat() `
3365+ * ` fs.lstat() `
3366+ * ` fs.exists() `
3367+ * ` fs.access() `
3368+ * ` fs.unlink() `
3369+ * ` fs.mkdir() `
3370+ * ` fs.rmdir() `
3371+ * ` fs.readdir() `
3372+
3373+ ** Promise methods (` fs/promises ` ):**
3374+
3375+ * ` fsPromises.readFile() `
3376+ * ` fsPromises.writeFile() `
3377+ * ` fsPromises.appendFile() `
3378+ * ` fsPromises.stat() `
3379+ * ` fsPromises.lstat() `
3380+ * ` fsPromises.access() `
3381+ * ` fsPromises.unlink() `
3382+ * ` fsPromises.mkdir() `
3383+ * ` fsPromises.rmdir() `
3384+ * ` fsPromises.readdir() `
3385+
3386+ ### Limitations
3387+
3388+ The mock file system has the following limitations:
3389+
3390+ * ** Symbolic links are not supported.** ` lstat() ` behaves identically to
3391+ ` stat() ` , and ` isSymbolicLink() ` always returns ` false ` .
3392+ * ** Dirent objects are not ` fs.Dirent ` instances.** The objects returned by
3393+ ` readdir({ withFileTypes: true }) ` have the same properties and methods as
3394+ ` fs.Dirent ` , but ` dirent instanceof fs.Dirent ` will return ` false ` .
3395+ * ** The following methods are not mocked:**
3396+ * ` fs.copyFile() ` / ` fs.copyFileSync() `
3397+ * ` fs.rename() ` / ` fs.renameSync() `
3398+ * ` fs.chmod() ` / ` fs.chmodSync() ` / ` fs.chown() ` / ` fs.chownSync() `
3399+ * ` fs.realpath() ` / ` fs.realpathSync() `
3400+ * ` fs.watch() ` / ` fs.watchFile() ` / ` fs.unwatchFile() `
3401+ * ` fs.open() ` / ` fs.openSync() ` and file descriptor operations
3402+ * ` fs.createReadStream() ` / ` fs.createWriteStream() `
3403+ * ** File permissions are not enforced.** All virtual files are created with
3404+ mode ` 0o644 ` and permission checks are not performed.
3405+ * ** File descriptors are not supported.** Operations that require file
3406+ descriptors will not work with virtual files.
3407+ * ** ` mkdir() ` return value.** When called with ` { recursive: true } ` ,
3408+ ` mkdir() ` returns the first directory path created (matching real ` fs `
3409+ behavior). Without ` recursive ` , it returns ` undefined ` .
3410+ * ** Recursive ` readdir() ` is not supported.** Calling ` readdir() ` with
3411+ ` { recursive: true } ` will throw ` ERR_INVALID_ARG_VALUE ` .
3412+
3413+ ### Stats object
3414+
3415+ Mock stats objects have the following properties with default values:
3416+
3417+ | Property | Type | Default |
3418+ | ------------------------------------------- | -------- | -------------------------------- |
3419+ | ` dev ` | ` number ` | ` 0 ` |
3420+ | ` ino ` | ` number ` | ` 0 ` |
3421+ | ` mode ` | ` number ` | File: ` 0o100644 ` , Dir: ` 0o40644 ` |
3422+ | ` nlink ` | ` number ` | ` 1 ` |
3423+ | ` uid ` | ` number ` | ` 0 ` |
3424+ | ` gid ` | ` number ` | ` 0 ` |
3425+ | ` rdev ` | ` number ` | ` 0 ` |
3426+ | ` size ` | ` number ` | Content length |
3427+ | ` blksize ` | ` number ` | ` 4096 ` |
3428+ | ` blocks ` | ` number ` | ` ceil(size / 512) ` |
3429+ | ` atime ` /` mtime ` /` ctime ` /` birthtime ` | ` Date ` | Creation time |
3430+ | ` atimeMs ` /` mtimeMs ` /` ctimeMs ` /` birthtimeMs ` | ` number ` | Creation time (ms) |
3431+
3432+ The stats object includes the following methods that return the expected
3433+ values for virtual files and directories:
3434+
3435+ * ` isFile() `
3436+ * ` isDirectory() `
3437+ * ` isBlockDevice() ` - always returns ` false `
3438+ * ` isCharacterDevice() ` - always returns ` false `
3439+ * ` isSymbolicLink() ` - always returns ` false `
3440+ * ` isFIFO() ` - always returns ` false `
3441+ * ` isSocket() ` - always returns ` false `
3442+
31093443## Class: ` TestsStream `
31103444
31113445<!-- YAML
@@ -4056,6 +4390,7 @@ Can be used to abort test subtasks when the test has been aborted.
40564390[ `--test-skip-pattern` ] : cli.md#--test-skip-pattern
40574391[ `--test-update-snapshots` ] : cli.md#--test-update-snapshots
40584392[ `--test` ] : cli.md#--test
4393+ [ `MockFileSystem` ] : #class-mockfilesystem
40594394[ `MockFunctionContext` ] : #class-mockfunctioncontext
40604395[ `MockPropertyContext` ] : #class-mockpropertycontext
40614396[ `MockTimers` ] : #class-mocktimers
0 commit comments