问题
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?
回答1:
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.
回答2:
Just derive from FileSystemWatcher
and add whatever you need to the interface:
public interface IFileSystemWatcherWrapper : IDisposable
{
bool EnableRaisingEvents { get; set; }
event FileSystemEventHandler Changed;
//...
}
public class FileSystemWatcherWrapper : FileSystemWatcher, IFileSystemWatcherWrapper
{
public FileSystemWatcherWrapper(string path, string filter)
: base(path, filter)
{
}
}
回答3:
As the FileSystemWatcher is not a sealed class, you can inherit from it and create an interface:
public class FileSystemWatcherWrapper : FileSystemWatcher, IFileSystemWatcherWrapper
{
//empty on purpose, doesnt need any code
}
public interface IFileSystemWatcherWrapper
{
event FileSystemEventHandler Created;
event FileSystemEventHandler Deleted;
bool EnableRaisingEvents { get; set; }
bool IncludeSubdirectories { get; set; }
string Path { get; set; }
string Filter { get; set; }
void Dispose();
}
}
Then you can unit test with Moq by Mock
来源:https://stackoverflow.com/questions/33254493/unit-testing-filesystemwatcher-how-to-programatically-fire-a-changed-event