Search code examples
c#multithreadingunit-testingfilesystemwatcher

How do I test that a FileSystemWatcher raises the correct events?


I am using a System.IO.FileSystemWatcher in one of my services. I want to test that when a file being monitored is changed, I get a notification.

I was thinking about having a background thread change the file. In the test, I would join on that thread. Then I can assert that the correct events are called. I could subscribe a callback to capture if the event was called.

I have not done any testing involving threads so I am not sure if this is the best way to handle it or if there are some built in ways in Moq or MSpec that will help test.


Solution

  • Moq or MSpec don't have anything specifically built in that will help you do this, except for some interesting syntax or features that will help you organize your test. I think you're on the right path.

    I'm curious how your service exposes file changed notifications. Does it expose them publicly for testing? Or is the FileSystemWatcher entirely hidden inside the service? If the service doesn't simply pass the event notification up and out, you should extract your file monitoring so that it can be easily tested.

    You can do that with .NET events or callbacks or whatever. No matter how you go about it, I would write the test something like this...

    [Subject("File monitoring")]
    public class When_a_monitored_file_is_changed
    {
        Establish context = () => 
        {
            // depending on your service file monitor design, you would
            // attach to your notification
            _monitor.FileChanged += () => _changed.Set();
    
            // or pass your callback in
            _monitor = new ServiceMonitor(() => _changed.Set());
        }
    
        Because of = () => // modify the monitored file;
    
        // Wait a reasonable amount of time for the notification to fire, but not too long that your test is a burden
        It should_raise_the_file_changed_event = () => _changed.WaitOne(TimeSpan.FromMilliseconds(100)).ShouldBeTrue();
    
        private static readonly ManualResetEvent _changed = new ManualResetEvent();
    }