Skip to main content

FileSystemWatcher

MockFileSystem ships a fully functional FileSystemWatcher replacement. It fires the same events as the real one - and crucially, it fires the OS-specific events that the real watcher fires on Linux, Windows and macOS.

Basic usage

var fileSystem = new MockFileSystem();
fileSystem.Initialize().WithSubdirectory("watched");

using var watcher = fileSystem.FileSystemWatcher.New("watched");
var deleted = new ManualResetEventSlim();

watcher.Deleted += (_, _) => deleted.Set();
watcher.EnableRaisingEvents = true;

fileSystem.Directory.Delete("watched");

await Expect.That(deleted.Wait(TimeSpan.FromSeconds(1))).IsTrue();

Reacting to triggered events from a test

MockFileSystem.Watcher.OnTriggered(...) returns an awaitable callback that fires after every IFileSystemWatcher event. Use it to assert what a watcher saw without subscribing to the event yourself:

MockFileSystem fileSystem = new();
fileSystem.Initialize().WithSubdirectory("watched");

using var watcher = fileSystem.FileSystemWatcher.New("watched");
watcher.EnableRaisingEvents = true;

using var triggered = fileSystem.Watcher.OnTriggered(
change => { /* inspect ChangeDescription */ });

fileSystem.File.WriteAllText("watched/a.txt", "hi");
ChangeDescription[] events = triggered.Wait(count: 1);

Event tables (per OS)

The watcher fires different events depending on whether the source/target is inside, nested under, deeply nested under, or outside the watched path. The table below lists every combination for both values of IncludeSubdirectories - it is also the contract that the MockFileSystem watcher honours when simulating other operating systems.

LocationDescription
_Entry was created or deleted
OutsideOutside the watching path
InsideImmediately inside the watching path
NestedNested under the watching path
DeepDeeply nested under the watching path

Each cell is IncludeSubdirectories = false / IncludeSubdirectories = true. When both values match, only one is shown. means no event is raised.

From → ToLinuxWindowsMac
_ → InsideCreatedCreatedCreated
Inside → _DeletedDeletedDeleted
_ → Nested– / Created– / Created– / Created
Nested → _– / Deleted– / Deleted– / Deleted
_ → Deep– / Created– / Created– / Created
Deep → _– / Deleted– / Deleted– / Deleted
Outside → InsideCreatedCreatedCreated
Inside → InsideRenamedRenamedRenamed
Inside → OutsideDeletedDeletedDeleted
Outside → Nested– / Created– / Created– / Created
Nested → Nested– / Renamed– / Renamed– / Renamed
Nested → Outside– / Deleted– / Deleted– / Deleted
Outside → Deep– / Created– / Created– / Created
Deep → Deep– / Renamed– / Renamed– / Renamed
Deep → Outside– / Deleted– / Deleted– / Deleted
Inside → NestedDeleted / RenamedDeleted / Deleted+CreatedRenamed
Inside → DeepDeleted / RenamedDeleted / Deleted+CreatedRenamed
Nested → InsideCreated / RenamedCreated / Deleted+CreatedCreated / Renamed
Deep → InsideCreated / RenamedCreated / Deleted+CreatedCreated / Renamed
Nested → Deep– / Renamed– / Deleted+Created– / Renamed
Deep → Nested– / Renamed– / Deleted+Created– / Renamed