Skip to content

MockFileSystemWatcher #854

Closed
Closed

Description

@CallumECameron

Is your feature request related to a problem? Please describe.
I'd like to be able to test code dependent on IFileSystemWatcher. Currently get a NotImplementedException when using MockFileSystem.FileSystemWatcher.

Describe the solution you'd like
An implementation of MockFileSystem.FileSystemWatcher that will fire appropriate events when files/directories are modified via MockFileSystem.File and MockFileSystem.Directory.

Activity

  1. fgreinacher commented on May 27, 2022

    @fgreinacher
    Contributor

    Thanks for writing the issue!

    We already discussed this and came to the conclusion that we don't want it in the core library. My suggestion here would be to use the existing extensibility points and provide your own implementation.

  2. added
    state: won't doIssues that won't be addressed. Reasons differ and will be provided as comments.
    and removed on May 27, 2022
  3. CallumECameron commented on Jun 6, 2022

    @CallumECameron
    Author

    Ah, okay, thank you. I have tried deriving from MockFileSystem and overriding the FileSystemWatcher property but I can't see any way to intercept calls to IMockFileDataAccessor to support the functionality of my file watcher mock (e.g. when a file is removed, the watcher will notify). I think I would need to reimplement MockFileSystem to add this behaviour unless you know of something else?

  4. fgreinacher commented on Jun 23, 2022

    @fgreinacher
    Contributor

    Sorry @CallumECameron, I forgot to get back to this after my vacation.

    At the moment I see two options:

    1. Mock the behavior of the file system watcher to fire the expected events independently of the file system actions.
    2. (Similar to what you said): Have a custom IFileSystem that intercepts calls and triggers the expected events. There is a good example at Make the test library open for extension #597 (comment).

    Does that help?

  5. CallumECameron commented on Jul 5, 2022

    @CallumECameron
    Author

    No problem, @fgreinacher, thanks for getting back to me. This helped, thank you.

  6. zivarah commented on May 14, 2023

    @zivarah
    Contributor

    @fgreinacher I am curious if there is a middle ground between trying to ship a fully functioning MockFileSystemWatcher and forcing the consumer to provide one just to allow their tests to run at all.

    For context: I have a class that accepts IFileSystem as an injected dependency. That code uses IFileSystemWatcher as well as various other IFileSystem APIs. I have two testing desires:

    1. Test APIs that are not reliant on the file system watching aspect at all
    2. Test behaviors that are triggered by the IFileSystemWatcher events

    But because we set up a file watcher at construction, the built-in MockFileSystem throws even for the APIs that don't use it.

    In order to test my non-watching behaviors, I have to provide some sort of IFileSystemFactory implementation. In order to test the IFileSystemWatcher event handlers, I'd obviously need to build that capability into my custom factory/watcher.

    The first piece is rather trivial, and I haven't gotten to the second piece yet.

    While I absolutely agree that we don't want the default behavior to be misleading/incomplete, it seems like we could provide a more useful opt-in option out of the box too.

    Would you be open to either of the below ideas?

    1. Ship a NoOpFileSystemWatcher that allows the dependent code to set up the file watcher and access any events/properties without any errors, but makes no attempts to actually trigger any events
    2. Ship a ManuallyTriggeredFileSystemWatcher that allows setup as above, but that also allows the test code to manually trigger specific events
      1. This avoids the need to deal with replicating exact file-watching behaviors while still providing an option for testing the event handlers

    I admittedly haven't attempted to actually develop either of these yet, but my gut instinct is that we'd be able to keep complexity fairly low. I'd be willing to take a stab at it if you are not opposed.

  7. vbreuss commented on May 16, 2023

    @vbreuss
    Member

    @zivarah : I created an alternative MockFileSystem in Testably.Abstractions, that uses the same interface as System.IO.Abstractions and which implements a fully-functional FileSystemWatcher.
    Nuget
    You can directly use this mocked filesystem in your tests without having to change your production code.

    A very basic example test (deletion of a file) that illustrates the functionality would be:

    	[Fact]
    	public void EnableRaisingEvents_WhenDirectoryGetsDeleted_ShouldTriggerCorrectFileSystemEventArgs()
    	{
    		MockFileSystem fileSystem = new();
    		fileSystem.InitializeIn("base-directory")
    			.WithSubdirectory("foo");
    		FileSystemEventArgs? result = null;
    		ManualResetEventSlim ms = new();
    		using IFileSystemWatcher fileSystemWatcher =
    			fileSystem.FileSystemWatcher.New(".");
    		fileSystemWatcher.Deleted += (_, eventArgs) =>
    		{
    			result = eventArgs;
    			ms.Set();
    		};
    		fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName;
    		fileSystemWatcher.EnableRaisingEvents = true;
    
    		fileSystem.Directory.Delete("foo");
    
    		ms.Wait(1000).Should().BeTrue();
    
    		result.Should().NotBeNull();
    		result!.ChangeType.Should().Be(WatcherChangeTypes.Deleted);
    		result.FullPath.Should().Be(fileSystem.Path.GetFullPath("foo"));
    	}
  8. fgreinacher commented on May 20, 2023

    @fgreinacher
    Contributor

    Would you be open to either of the below ideas?

    @zivarah Yes, absolutely! Both options seem fine to me, as long as we keep them opt-in. Feel free to submit a PR for discussing how we can integrate this nicely!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: testinghelpersIssues that address the testing helpersstate: won't doIssues that won't be addressed. Reasons differ and will be provided as comments.type: enhancementIssues that propose new functionality

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions