单元测试 FileSystemWatcher:如何以编程方式触发已更改的事件?
Unit-testing FileSystemWatcher: How to programatically fire a changed event?
我有一个 FileSystemWatcher
监视目录的变化,当其中有一个新的 XML 文件时,它会解析该文件并对其进行处理。
我的项目中有一些示例 XML 文件,我将它们用于我编写的解析器的单元测试目的。
我正在寻找一种方法来使用示例 XML 文件来测试 FileSystemWatcher
。
是否可以通过编程方式创建事件(以某种方式涉及 XML 文件)以触发 FSW.Changed
事件?
我认为你在这里采取了错误的方法。
您不应该尝试直接对 FileSystemWatcher
class 进行单元测试(您不能 - 您无法控制它!)。相反,您可以尝试以下操作:
1) 为 FileSystemWatcher
class 编写一个包装器 class,仅 将其功能委托给 [=14= 的实例].这是一个方法和一个事件的示例,根据需要添加更多成员:
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; }
}
}
(请注意 FileSystemWatcher
的实例是如何传递给 class 构造函数的;您可以在构造包装器时即时创建一个新实例)
2) 为 class:
提取接口
public interface IFileSystemWatcherWrapper
{
event FileSystemEventHandler Changed;
bool EnableRaisingEvents { get; set; }
}
//and therefore...
public class FileSystemWatcherWrapper : IFileSystemWatcherWrapper
3) 让你的 class 依赖接口:
public class TheClassThatActsOnFilesystemChanges
{
private readonly IFileSystemWatcherWrapper fileSystemWatcher;
public TheClassThatActsOnFilesystemChanges(IFileSystemWatcherWrapper fileSystemWatcher)
{
this.fileSystemWatcher = fileSystemWatcher;
fileSystemWatcher.Changed += (sender, args) =>
{
//Do something...
};
}
}
4) 在应用程序初始化时,使用任何依赖注入引擎实例化您的 class,或者只是做可怜的注入:
var theClass = new TheClassThatActsOnFilesystemChanges(
new FileSystemWatcherWrapper(new FileSystemWatcher()));
5) 现在继续为 TheClassThatActsOnFilesystemChanges
编写单元测试,方法是创建一个 IFileSystemWatcherWrapper
的模拟,它可以根据您的意愿触发事件!您可以为此使用任何模拟引擎,例如 Moq.
底线:
当您依赖于您无法控制的 class 时,and/or 无法进行有意义的单元测试,请使用适当的接口对其进行环绕,并依赖于界面。你的包装器太薄了,如果你不能对它进行单元测试,它并没有真正的伤害,而你的客户端 classes 现在可以正确地进行单元测试。
只需从 FileSystemWatcher
派生并向界面添加您需要的任何内容:
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)
{
}
}
由于 FileSystemWatcher 不是密封的 class,您可以继承它并创建一个接口:
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();
}
}
然后你可以通过 Mock 对 Moq 进行单元测试
我有一个 FileSystemWatcher
监视目录的变化,当其中有一个新的 XML 文件时,它会解析该文件并对其进行处理。
我的项目中有一些示例 XML 文件,我将它们用于我编写的解析器的单元测试目的。
我正在寻找一种方法来使用示例 XML 文件来测试 FileSystemWatcher
。
是否可以通过编程方式创建事件(以某种方式涉及 XML 文件)以触发 FSW.Changed
事件?
我认为你在这里采取了错误的方法。
您不应该尝试直接对 FileSystemWatcher
class 进行单元测试(您不能 - 您无法控制它!)。相反,您可以尝试以下操作:
1) 为 FileSystemWatcher
class 编写一个包装器 class,仅 将其功能委托给 [=14= 的实例].这是一个方法和一个事件的示例,根据需要添加更多成员:
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; }
}
}
(请注意 FileSystemWatcher
的实例是如何传递给 class 构造函数的;您可以在构造包装器时即时创建一个新实例)
2) 为 class:
提取接口public interface IFileSystemWatcherWrapper
{
event FileSystemEventHandler Changed;
bool EnableRaisingEvents { get; set; }
}
//and therefore...
public class FileSystemWatcherWrapper : IFileSystemWatcherWrapper
3) 让你的 class 依赖接口:
public class TheClassThatActsOnFilesystemChanges
{
private readonly IFileSystemWatcherWrapper fileSystemWatcher;
public TheClassThatActsOnFilesystemChanges(IFileSystemWatcherWrapper fileSystemWatcher)
{
this.fileSystemWatcher = fileSystemWatcher;
fileSystemWatcher.Changed += (sender, args) =>
{
//Do something...
};
}
}
4) 在应用程序初始化时,使用任何依赖注入引擎实例化您的 class,或者只是做可怜的注入:
var theClass = new TheClassThatActsOnFilesystemChanges(
new FileSystemWatcherWrapper(new FileSystemWatcher()));
5) 现在继续为 TheClassThatActsOnFilesystemChanges
编写单元测试,方法是创建一个 IFileSystemWatcherWrapper
的模拟,它可以根据您的意愿触发事件!您可以为此使用任何模拟引擎,例如 Moq.
底线:
当您依赖于您无法控制的 class 时,and/or 无法进行有意义的单元测试,请使用适当的接口对其进行环绕,并依赖于界面。你的包装器太薄了,如果你不能对它进行单元测试,它并没有真正的伤害,而你的客户端 classes 现在可以正确地进行单元测试。
只需从 FileSystemWatcher
派生并向界面添加您需要的任何内容:
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)
{
}
}
由于 FileSystemWatcher 不是密封的 class,您可以继承它并创建一个接口:
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();
}
}
然后你可以通过 Mock 对 Moq 进行单元测试