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