C# 单元测试FileSystemWatcher:如何通过编程触发已更改的事件?
我有一个C# 单元测试FileSystemWatcher:如何通过编程触发已更改的事件?,c#,.net,unit-testing,filesystemwatcher,C#,.net,Unit Testing,Filesystemwatcher,我有一个FileSystemWatcher监视目录中的更改,当目录中有新的XML文件时,它会解析该文件并对其进行处理 我的项目中有一些示例XML文件,用于我编写的解析器的单元测试 我正在寻找一种使用示例XML文件来测试FileSystemWatcher的方法 是否可以通过编程方式创建事件(以某种方式涉及XML文件)来触发FSW.Changed事件?我认为您在这里采取了错误的方法 您不应该尝试直接对FileSystemWatcher类进行单元测试(您不能-您无法控制它!)。相反,您可以尝试以下操作
FileSystemWatcher
监视目录中的更改,当目录中有新的XML文件时,它会解析该文件并对其进行处理
我的项目中有一些示例XML文件,用于我编写的解析器的单元测试
我正在寻找一种使用示例XML文件来测试FileSystemWatcher
的方法
是否可以通过编程方式创建事件(以某种方式涉及XML文件)来触发
FSW.Changed
事件?我认为您在这里采取了错误的方法
您不应该尝试直接对FileSystemWatcher
类进行单元测试(您不能-您无法控制它!)。相反,您可以尝试以下操作:
1) 为FileSystemWatcher
类编写一个包装类,该类仅将其功能委托给FileSystemWatcher
的实例。下面是一个包含一个方法和一个事件的示例,请根据需要添加更多成员:
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
的实例是如何传递给类构造函数的;您可以在构建包装器时动态创建一个新实例)
2) 提取类的接口:
public interface IFileSystemWatcherWrapper
{
event FileSystemEventHandler Changed;
bool EnableRaisingEvents { get; set; }
}
//and therefore...
public class FileSystemWatcherWrapper : IFileSystemWatcherWrapper
3) 使您的类依赖于接口:
public class TheClassThatActsOnFilesystemChanges
{
private readonly IFileSystemWatcherWrapper fileSystemWatcher;
public TheClassThatActsOnFilesystemChanges(IFileSystemWatcherWrapper fileSystemWatcher)
{
this.fileSystemWatcher = fileSystemWatcher;
fileSystemWatcher.Changed += (sender, args) =>
{
//Do something...
};
}
}
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)
{
}
}
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();
}
}
4) 在应用程序初始化时,使用任何依赖项注入引擎实例化您的类,或者只执行穷人的注入:
var theClass = new TheClassThatActsOnFilesystemChanges(
new FileSystemWatcherWrapper(new FileSystemWatcher()));
5) 现在,继续为执行文件系统更改的类编写单元测试,方法是创建一个根据您的意愿触发事件的IFileSystemWatcherRapper
模拟!例如,您可以使用任何模拟引擎
底线:
当你对一个你不能控制和/或不能进行有意义的单元测试的类有依赖性时,用一个合适的接口写一个包装,并依赖这个接口。您的包装非常薄,如果您不能对其进行单元测试,它实际上不会造成任何伤害,而您的客户机类现在可以进行正确的单元测试。只需从
FileSystemWatcher
派生并向接口添加所需的内容即可:
public class TheClassThatActsOnFilesystemChanges
{
private readonly IFileSystemWatcherWrapper fileSystemWatcher;
public TheClassThatActsOnFilesystemChanges(IFileSystemWatcherWrapper fileSystemWatcher)
{
this.fileSystemWatcher = fileSystemWatcher;
fileSystemWatcher.Changed += (sender, args) =>
{
//Do something...
};
}
}
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)
{
}
}
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();
}
}
由于FileSystemWatcher不是密封类,因此您可以从中继承并创建接口:
public class TheClassThatActsOnFilesystemChanges
{
private readonly IFileSystemWatcherWrapper fileSystemWatcher;
public TheClassThatActsOnFilesystemChanges(IFileSystemWatcherWrapper fileSystemWatcher)
{
this.fileSystemWatcher = fileSystemWatcher;
fileSystemWatcher.Changed += (sender, args) =>
{
//Do something...
};
}
}
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)
{
}
}
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();
}
}
然后你可以通过Moq by Mock进行单元测试你不能打开文件并向xml文件中添加一些测试吗,例如添加注释,然后将其返回到磁盘请共享要测试的FSW代码并尝试单元测试首先,你可以为FileSystemWatcher创建一个模拟对象并将其传递给你的类,然后调用模拟的已更改事件。另一个选项是,您可以创建一个从FileSystemWatcher派生的模拟类,并根据需要重写已更改的方法,然后将其传递给类进行测试。在测试中,另一个选项是,当代码监视文件更改时,在并行任务中将真实文件复制并粘贴到您的目录中。@mecek如何调用模拟的更改事件?FSW是精确模拟的棘手问题。确保在您的测试计划中包括延迟、重复通知、来自其他软件的锁定以及丢失的通知。我可能还遗漏了其他注意事项。您是否在示例中链接事件?我似乎不能让事情像那样连成一条龙。有什么我可能遗漏的吗?谢谢!请纠正小错误-在FileSystemWatcherRapper构造函数中添加“this.watcher=watcher;”。@BluE您可以将事件触发的公共方法添加到
FileSystemWatcherRapper
。