【问题标题】:How can I use FileSystemWatcher from System.IO.Abstractions to monitor a mock filesystem?如何使用 System.IO.Abstractions 中的 FileSystemWatcher 来监视模拟文件系统?
【发布时间】:2017-10-31 09:15:13
【问题描述】:

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

似乎跟踪这些事件的最佳方法是使用FileSystemWatcher。为了使类可测试,我使用System.IO.Abstractions 并在构造过程中将IFileSystem 的实例注入到类中。我想要的是创建一些行为类似于FileSystemWatcher 但监视注入的IFileSystem 的更改,而不是直接监视真正的文件系统。

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

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

【问题讨论】:

  • 我能问下反对票的用途是什么吗?
  • 我认为这不可能。 FileSystemWatcher 类根本不可扩展(甚至没有任何有用的虚拟方法)。取而代之的是-在具有Created等事件的某些接口后面抽象出FileSystemWatcher,并在您的单元测试中手动触发这些事件。因此,在将目录添加到您的 IFileSystem 之后 - 自己触发相应的事件。
  • 这是一种方式,但我希望我不必诉诸于此。我没想到他们会直接扩展FileSystemWatcher,但可能会有一些类似于工厂IFileSystem =&gt; IFileSystemWatcher 的东西,它检查提供的IFileSystem 是真实的还是模拟的。如果是真的,则返回标准 FileSystemWatcher 的包装器。如果是 mock,则返回一个自定义类来监视对 mock 文件系统的更改。
  • 我猜你必须自己实现它,或者提交关于它的 github 问题并希望作者能够实现它。已经存在关于实现模拟 FileSystemWatcher 的问题:github.com/tathamoddie/System.IO.Abstractions/issues/167,它已经 1 岁了,所以我不希望很快得到结果。
  • 啊,干杯。太糟糕了。

标签: c# filesystems filesystemwatcher


【解决方案1】:

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

我定义了一个扩展IFileSystemIWatchableFileSystem 接口:

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

出于生产目的,我将其实现为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();
}

在我的单元测试类中,我将它实现为MockWatchableFileSystem,它将MockFileSystemMock&lt;FileSystemWatcherBase&gt; 公开为我可以用于在我的测试中安排和断言的属性:

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

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

【讨论】:

  • 如果我稍后调用 .AsMock.AddFile 方法,会调用 Created 事件吗?
猜你喜欢
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多