简体   繁体   English

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

[英]How can I use FileSystemWatcher from System.IO.Abstractions to monitor a mock filesystem?

I'm trying to create a class to monitor USB device arrivals and removals on Linux. 我正在尝试创建一个类来监视Linux上USB设备的到来和移除。 On Linux, USB devices are represented as device files under /dev/bus/usb which are created/deleted in response to these events. 在Linux上,USB设备表示为/dev/bus/usb下的设备文件,这些文件是为响应这些事件而创建/删除的。

It seems the best way to track these events is using FileSystemWatcher . 似乎最好的跟踪这些事件的方法是使用FileSystemWatcher To make the class testable, I'm using System.IO.Abstractions and injecting an instance of IFileSystem to the class during construction. 为了使该类可测试,我使用了System.IO.Abstractions并将IFileSystem的实例注入到该类中。 What I want is to create something that behaves like a FileSystemWatcher but monitors changes to the injected IFileSystem , not the real file-system directly. 我想要创建的行为类似于FileSystemWatcher但是监视对注入的IFileSystem更改,而不是直接监视实际的文件系统。

Looking at FileSystemWatcherBase and FileSystemWatcherWrapper from System.IO.Abstractions , I'm not sure how to do this. System.IO.Abstractions查看FileSystemWatcherBaseFileSystemWatcherWrapper ,我不确定如何执行此操作。 At the moment I have this (which I know is wrong): 目前,我有这个(我知道这是错误的):

public DevMonitor(
    [NotNull] IFileSystem fileSystem,
    [NotNull] IDeviceFileParser deviceFileParser,
    [NotNull] ILogger logger,
    [NotNull] string devDirectoryPath = DefaultDevDirectoryPath)
{
    Raise.ArgumentNullException.IfIsNull(logger, nameof(logger));
    Raise.ArgumentNullException.IfIsNull(devDirectoryPath, nameof(devDirectoryPath));

    _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
    _deviceFileParser = deviceFileParser ?? throw new ArgumentNullException(nameof(deviceFileParser));
    _logger = logger.ForContext<DevMonitor>();
    _watcher = new FileSystemWatcherWrapper(devDirectoryPath);
}

In light of the fact that System.IO.Abstractions doesn't seem to support this yet, I went with this: 鉴于System.IO.Abstractions似乎尚不支持这一点,我同意了:

I defined an IWatchableFileSystem interface that extends IFileSystem : 我定义了一个IWatchableFileSystem接口,该接口扩展了IFileSystem

/// <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();
}

For production purposes I implement this as WatchableFileSystem : 出于生产目的,我将其实现为WatchableFileSystem

/// <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();
}

Within my unit test class I implement it as MockWatchableFileSystem which exposes MockFileSystem and Mock<FileSystemWatcherBase> as properties which I can use for arranging & asserting within my tests: 在我的单元测试类中,我将其实现为MockWatchableFileSystem ,将MockFileSystemMock<FileSystemWatcherBase>公开为可用于在测试中进行安排和声明的属性:

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;
}

Finally, in my client class I can just do: 最后,在我的客户类中,我可以做:

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;
}

During tests, the client gets an IWatchableFileSystem that wraps MockFileSystem and returns a mock instance of FileSystemWatcherBase . 在测试过程中,客户端将获得一个IWatchableFileSystem ,该IWatchableFileSystem包裹类包装了MockFileSystem并返回FileSystemWatcherBase的模拟实例。 During production, it gets an IWatchableFileSystem that wraps FileSystem and generates unique instances of FileSystemWatcher . 在生产过程中,它得到的IWatchableFileSystem一个封装FileSystem ,并产生独特的实例FileSystemWatcher

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM