Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit-testing FileSystemWatcher: How to programatically fire a changed event?

I have a FileSystemWatcher watching a directory for changes, and when there's a new XML file in it, it parses that file and does something with it.

I have a few sample XML files in my project that I am using for unit-testing purposes for the parser I wrote.

I'm looking for a way to use the sample XML files also to test the FileSystemWatcher.

Is it possible to programatically create an event (somehow involving the XML file) in order to trigger the FSW.Changed event?

like image 745
Near Avatar asked Oct 21 '15 08:10

Near


People also ask

Which event handler is suggested when a file is changed?

The FileSystemWatcher provides us with the event handlers to capture events like renamed, deleted, created and changed.

How does FileSystemWatcher work?

The FileSystemWatcher class in the System.IO namespace can be used to monitor changes to the file system. It watches a file or a directory in your system for changes and triggers events when changes occur. In order for the FileSystemWatcher to work, you should specify a directory that needs to be monitored.

Is FileSystemWatcher multithreaded?

Nope, filesystemwatchers run on their own thread.

How to use file watcher in c#?

To watch for changes in all files, set the Filter property to an empty string ("") or use wildcards ("*. *"). To watch a specific file, set the Filter property to the file name. For example, to watch for changes in the file MyDoc.


1 Answers

I think that you are taking the wrong approach here.

You should not try to directly unit test the FileSystemWatcher class (you can't - you have no control on it!). Instead, you can try the following:

1) Write a wrapper class for the FileSystemWatcher class that only delegates its functionality to an instance of FileSystemWatcher. Here's an example with one method and one event, add more members as required:

public class FileSystemWatcherWrapper
{
    private readonly FileSystemWatcher watcher;

    public event FileSystemEventHandler Changed;

    public FileSystemWatcherWrapper(FileSystemWatcher watcher)
    {
        this.watcher = watcher
        watcher.Changed += this.Changed;
    }

    public bool EnableRaisingEvents
    {
        get { return watcher.EnableRaisingEvents; }
        set { watcher.EnableRaisingEvents = value; }
    }
}

(Note how the instance of FileSystemWatcher is passed to the class constructor; you could create a new instance on the fly instead when the wrapper is constructed)

2) Extract an interface for the class:

public interface IFileSystemWatcherWrapper
{
    event FileSystemEventHandler Changed;
    bool EnableRaisingEvents { get; set; }
}

//and therefore...

public class FileSystemWatcherWrapper : IFileSystemWatcherWrapper

3) Make your class dependant on the interface:

public class TheClassThatActsOnFilesystemChanges
{
    private readonly IFileSystemWatcherWrapper fileSystemWatcher;

    public TheClassThatActsOnFilesystemChanges(IFileSystemWatcherWrapper fileSystemWatcher)
    {
        this.fileSystemWatcher = fileSystemWatcher;

        fileSystemWatcher.Changed += (sender, args) =>
        {
            //Do something...
        };
    }
}

4) At application initialization time, instantiate your class using any dependency injection engine, or just do poor man's injection:

var theClass = new TheClassThatActsOnFilesystemChanges(
    new FileSystemWatcherWrapper(new FileSystemWatcher()));

5) Now go ahead and write unit tests for TheClassThatActsOnFilesystemChanges by creating a mock of IFileSystemWatcherWrapper that fires events at your will! You can use any mocking engine for that, for example Moq.

The bottom line:

When you have a dependency on a class that you don't control and/or can't be meaningfully unit tested, write a wrap around it with a proper interface, and depend on the interface. Your wrapper is so thin that it doesn't really hurt if you can't unit test it, while your client classes can now be properly unit tested.

like image 105
Konamiman Avatar answered Sep 21 '22 05:09

Konamiman