单元测试FileSystemWatcher:如何以编程方式触发已更改的事件

本文关键字:事件 方式触 编程 FileSystemWatcher 单元测试 | 更新日期: 2023-09-27 18:18:41

我有一个FileSystemWatcher监视目录的更改,当其中有一个新的XML文件时,它解析该文件并对其进行处理。

在我的项目中有几个示例XML文件,我将它们用于我编写的解析器的单元测试目的。

我正在寻找一种方法来使用样例XML文件也测试FileSystemWatcher

是否有可能以编程方式创建一个事件(以某种方式涉及XML文件),以触发FSW.Changed事件?

单元测试FileSystemWatcher:如何以编程方式触发已更改的事件

我认为你的做法是错误的。

您不应该尝试直接对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...
        };
    }
}
4)在应用程序初始化时,使用任何依赖注入引擎实例化你的类,或者只是做穷人的注入:
var theClass = new TheClassThatActsOnFilesystemChanges(
    new FileSystemWatcherWrapper(new FileSystemWatcher()));

5)现在继续为TheClassThatActsOnFilesystemChanges编写单元测试,创建一个IFileSystemWatcherWrapper的模拟,可以随意触发事件!您可以使用任何模拟引擎,例如Moq。

当你对一个你无法控制和/或不能进行有意义的单元测试的类有依赖时,用一个合适的接口来封装它,并依赖这个接口。您的包装器非常薄,即使无法对其进行单元测试也不会有什么影响,而您的客户端类现在可以进行适当的单元测试。

由于FileSystemWatcher不是一个密封类,您可以从它继承并创建一个接口:

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派生,并添加任何您需要的接口:

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)
    {
    }
}