C# 如何使用System.IO.Abstractions中的FileSystemWatcher监视模拟文件系统?

C# 如何使用System.IO.Abstractions中的FileSystemWatcher监视模拟文件系统?,c#,filesystems,filesystemwatcher,C#,Filesystems,Filesystemwatcher,我正在尝试创建一个类来监视Linux上USB设备的到达和删除。在Linux上,USB设备表示为/dev/bus/USB下的设备文件,这些文件是为响应这些事件而创建/删除的 跟踪这些事件的最佳方法似乎是使用FileSystemWatcher。为了使类可测试,我在构建过程中使用并向类注入IFileSystem的实例。我想要的是创建一个行为类似于文件系统观察者的东西,但是监视对注入的IFileSystem的更改,而不是直接监视真正的文件系统 查看System.IO.Abstractions中的File

我正在尝试创建一个类来监视Linux上USB设备的到达和删除。在Linux上,USB设备表示为
/dev/bus/USB
下的设备文件,这些文件是为响应这些事件而创建/删除的

跟踪这些事件的最佳方法似乎是使用
FileSystemWatcher
。为了使类可测试,我在构建过程中使用并向类注入
IFileSystem
的实例。我想要的是创建一个行为类似于
文件系统观察者的东西,但是监视对注入的
IFileSystem
的更改,而不是直接监视真正的文件系统

查看
System.IO.Abstractions
中的
FileSystemWatcherBase
filesystemwatcherrapper
,我不知道该怎么做。目前我有这个(我知道这是错误的):

公共DevMonitor(
[NotNull]IFileSystem文件系统,
[NotNull]IDeviceFileParser设备文件解析器,
[NotNull]ILogger记录器,
[NotNull]字符串devDirectoryPath=DefaultDevDirectoryPath)
{
Raise.ArgumentNullException.ifisnall(记录器,名称(记录器));
Raise.ArgumentNullException.ifisnall(devDirectoryPath,name of(devDirectoryPath));
_fileSystem=fileSystem??抛出新的ArgumentNullException(文件系统名称));
_deviceFileParser=deviceFileParser??抛出新的ArgumentNullException(nameof(deviceFileParser));
_logger=logger.ForContext();
_watcher=newfilesystemwatcherrapper(devDirectoryPath);
}

鉴于
System.IO.Abstractions
似乎还不支持这一点,我选择了以下内容:

我定义了一个扩展IFileSystem的
IWatchableFileSystem
接口:

/// <summary>
/// Represents a(n) <see cref="IFileSystem" /> that can be watched for changes.
/// </summary>
public interface IWatchableFileSystem : IFileSystem
{
    /// <summary>
    /// Creates a <c>FileSystemWatcher</c> that can be used to monitor changes to this file system.
    /// </summary>
    /// <returns>A <c>FileSystemWatcher</c>.</returns>
    FileSystemWatcherBase CreateWatcher();
}
/// <inheritdoc />
public sealed class WatchableFileSystem : IWatchableFileSystem
{
    private readonly IFileSystem _fileSystem;

    /// <summary>
    /// Initializes a new instance of the <see cref="WatchableFileSystem" /> class.
    /// </summary>
    public WatchableFileSystem() => _fileSystem = new FileSystem();

    /// <inheritdoc />
    public DirectoryBase Directory => _fileSystem.Directory;

    /// <inheritdoc />
    public IDirectoryInfoFactory DirectoryInfo => _fileSystem.DirectoryInfo;

    /// <inheritdoc />
    public IDriveInfoFactory DriveInfo => _fileSystem.DriveInfo;

    /// <inheritdoc />
    public FileBase File => _fileSystem.File;

    /// <inheritdoc />
    public IFileInfoFactory FileInfo => _fileSystem.FileInfo;

    /// <inheritdoc />
    public PathBase Path => _fileSystem.Path;

    /// <inheritdoc />
    public FileSystemWatcherBase CreateWatcher() => new FileSystemWatcher();
}
在我的单元测试类中,我将其实现为
MockWatchableFileSystem
,它将
MockFileSystem
Mock
公开为属性,我可以使用这些属性在测试中安排和断言:

private class MockWatchableFileSystem : IWatchableFileSystem
{
    /// <inheritdoc />
    public MockWatchableFileSystem()
    {
        Watcher = new Mock<FileSystemWatcherBase>();
        AsMock = new MockFileSystem();

        AsMock.AddDirectory("/dev/bus/usb");
        Watcher.SetupAllProperties();
    }

    public MockFileSystem AsMock { get; }

    /// <inheritdoc />
    public DirectoryBase Directory => AsMock.Directory;

    /// <inheritdoc />
    public IDirectoryInfoFactory DirectoryInfo => AsMock.DirectoryInfo;

    /// <inheritdoc />
    public IDriveInfoFactory DriveInfo => AsMock.DriveInfo;

    /// <inheritdoc />
    public FileBase File => AsMock.File;

    /// <inheritdoc />
    public IFileInfoFactory FileInfo => AsMock.FileInfo;

    /// <inheritdoc />
    public PathBase Path => AsMock.Path;

    public Mock<FileSystemWatcherBase> Watcher { get; }

    /// <inheritdoc />
    public FileSystemWatcherBase CreateWatcher() => Watcher.Object;
}

在测试期间,客户机将获得一个
IWatchableFileSystem
,它包装
MockFileSystem
,并返回
FileSystemWatcherBase
的模拟实例。在生产过程中,它会得到一个
IWatchableFileSystem
,它包装
文件系统
,并生成
FileSystemWatcher的唯一实例

我能问一下否决票是为了什么吗?我认为这是不可能的。FileSystemWatcher类根本不可扩展(甚至没有任何有用的虚拟方法)。相反,将FileSystemWatcher抽象到某个接口后面,该接口创建了
etc事件,并在单元测试中手动触发这些事件。因此,在将目录添加到您的
IFileSystem
之后,您可以自己触发相应的事件。这是一种方法,但我希望我不必求助于此。我不希望他们直接扩展
FileSystemWatcher
,但可能会有类似工厂
IFileSystem=>IFileSystemWatcher
的东西,该工厂检查提供的
IFileSystem
是真实的还是模拟的。如果为真,则返回标准
FileSystemWatcher
的包装。如果是mock,则返回一个自定义类,该类监视对mock文件系统的更改。我想您必须自己实现它,或者解决github的问题,并希望作者能够实现它。已经有一个关于实现mock FileSystemWatcher的问题:,已经有一年了,所以我不希望很快有结果。啊,干杯。那太糟糕了。
private class MockWatchableFileSystem : IWatchableFileSystem
{
    /// <inheritdoc />
    public MockWatchableFileSystem()
    {
        Watcher = new Mock<FileSystemWatcherBase>();
        AsMock = new MockFileSystem();

        AsMock.AddDirectory("/dev/bus/usb");
        Watcher.SetupAllProperties();
    }

    public MockFileSystem AsMock { get; }

    /// <inheritdoc />
    public DirectoryBase Directory => AsMock.Directory;

    /// <inheritdoc />
    public IDirectoryInfoFactory DirectoryInfo => AsMock.DirectoryInfo;

    /// <inheritdoc />
    public IDriveInfoFactory DriveInfo => AsMock.DriveInfo;

    /// <inheritdoc />
    public FileBase File => AsMock.File;

    /// <inheritdoc />
    public IFileInfoFactory FileInfo => AsMock.FileInfo;

    /// <inheritdoc />
    public PathBase Path => AsMock.Path;

    public Mock<FileSystemWatcherBase> Watcher { get; }

    /// <inheritdoc />
    public FileSystemWatcherBase CreateWatcher() => Watcher.Object;
}
public DevMonitor(
    [NotNull] IWatchableFileSystem fileSystem,
    [NotNull] IDeviceFileParser deviceFileParser,
    [NotNull] ILogger logger,
    [NotNull] string devDirectoryPath = DefaultDevDirectoryPath)
{
    // ...
    _watcher = fileSystem.CreateWatcher();

    _watcher.IncludeSubdirectories = true;
    _watcher.EnableRaisingEvents = true;
    _watcher.Path = devDirectoryPath;
}