You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何设置文件系统Mock返回DriveInfo?解决只读属性编译问题

如何Mock只读属性的DriveInfo用于文件系统单元测试

问题核心在于原生DriveInfo的DriveType和AvailableFreeSpace是非虚拟的只读属性,Mock框架(如Moq)无法直接修改或拦截这些属性的返回值。解决思路是通过抽象层解耦,绕过原生DriveInfo的限制。

以下是两种可行方案:


方案1:使用现成的文件系统抽象库(推荐)

直接用System.IO.Abstractions库,它已经为所有System.IO类型提供了可Mock的抽象接口,包括IDriveInfo和IFileSystem,无需自己重复造轮子。

步骤1:安装依赖

通过NuGet安装:

  • System.IO.Abstractions(核心抽象库)
  • Moq(Mock框架)

步骤2:重构业务代码

让FileStatsUtility依赖IFileSystem而非原生类型:

public class FileStatsUtility
{
    private readonly IFileSystem _fileSystem;

    public FileStatsUtility(IFileSystem fileSystem)
    {
        _fileSystem = fileSystem;
    }

    public long GetFreeSpaceMb(string drivePath)
    {
        var driveInfo = _fileSystem.DriveInfo.FromDriveName(drivePath);
        
        if (driveInfo.DriveType != DriveType.Fixed)
        {
            throw new InvalidOperationException("仅支持固定磁盘");
        }
        
        return driveInfo.AvailableFreeSpace / (1024 * 1024);
    }
}

步骤3:编写Mock测试

直接MockIDriveInfo并注入到IFileSystem中:

[TestClass]
public class FileStatsUtilityTests
{
    [TestMethod]
    public void GetFreeSpaceMb_ReturnsCorrectValue_ForFixedDrive()
    {
        // Arrange
        var mockDrive = new Mock<IDriveInfo>();
        // 设置只读属性的返回值
        mockDrive.Setup(d => d.DriveType).Returns(DriveType.Fixed);
        mockDrive.Setup(d => d.AvailableFreeSpace).Returns(500 * 1024 * 1024); // 500MB

        var mockFileSystem = new Mock<IFileSystem>();
        mockFileSystem.Setup(f => f.DriveInfo.FromDriveName("C:\\"))
                      .Returns(mockDrive.Object);

        var utility = new FileStatsUtility(mockFileSystem.Object);

        // Act
        var result = utility.GetFreeSpaceMb("C:\\");

        // Assert
        Assert.AreEqual(500, result);
    }
}

方案2:手动封装DriveInfo抽象

如果不想引入第三方库,可以自己定义抽象接口和包装类:

步骤1:定义抽象接口

public interface IDriveInfo
{
    DriveType DriveType { get; }
    long AvailableFreeSpace { get; }
    // 根据业务需求添加其他属性/方法
}

步骤2:实现包装类

用原生DriveInfo实现上述接口,供生产环境使用:

public class DriveInfoWrapper : IDriveInfo
{
    private readonly DriveInfo _innerDrive;

    public DriveInfoWrapper(string drivePath)
    {
        _innerDrive = new DriveInfo(drivePath);
    }

    public DriveType DriveType => _innerDrive.DriveType;
    public long AvailableFreeSpace => _innerDrive.AvailableFreeSpace;
}

步骤3:修改业务类依赖

让FileStatsUtility依赖IDriveInfo的创建逻辑(比如注入工厂或直接依赖Func<string, IDriveInfo>):

public class FileStatsUtility
{
    private readonly Func<string, IDriveInfo> _driveInfoFactory;

    public FileStatsUtility(Func<string, IDriveInfo> driveInfoFactory)
    {
        _driveInfoFactory = driveInfoFactory;
    }

    public long GetFreeSpaceMb(string drivePath)
    {
        var driveInfo = _driveInfoFactory(drivePath);
        
        if (driveInfo.DriveType != DriveType.Fixed)
        {
            throw new InvalidOperationException("仅支持固定磁盘");
        }
        
        return driveInfo.AvailableFreeSpace / (1024 * 1024);
    }
}

步骤4:编写Mock测试

MockIDriveInfo并传入工厂委托:

[TestMethod]
public void GetFreeSpaceMb_ReturnsCorrectValue_ForFixedDrive()
{
    // Arrange
    var mockDrive = new Mock<IDriveInfo>();
    mockDrive.Setup(d => d.DriveType).Returns(DriveType.Fixed);
    mockDrive.Setup(d => d.AvailableFreeSpace).Returns(500 * 1024 * 1024);

    var utility = new FileStatsUtility(_ => mockDrive.Object);

    // Act
    var result = utility.GetFreeSpaceMb("C:\\");

    // Assert
    Assert.AreEqual(500, result);
}

内容的提问来源于stack exchange,提问作者Lucas Hofman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 13:52:38