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

如何Mock ExcelReaderFactory.CreateReader并解决xUnit+Moq测试报错

解决xUnit+Moq测试Excel读取时的编码、Mock类型和文件签名问题

咱们一步步拆解你遇到的三个问题,逐个解决:

1. 先搞定「No data is available for encoding 1252」错误

这个问题是因为.NET Core/.NET 5+之后,默认不再包含Windows专属的旧编码(比如1252),需要手动引入并注册:

  • 先给测试项目安装NuGet包:System.Text.Encoding.CodePages
  • 在测试类的构造函数或者测试初始化代码里添加编码注册:
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

2. 修复「Object cannot be converted to IExcelDataReader」的Mock错误

你之前的Mock Returns写法搞错了泛型参数,Returns<IExcelDataReader>的Func参数应该对应输入的MemoryStream,而不是返回类型。调整成这样:

mockExcelDataMock.Setup(data => data.CreateReader(It.IsAny<MemoryStream>()))
    .Returns((MemoryStream _) => {
        var mockReader = new Mock<IExcelDataReader>();
        // 这里可以按需设置mockReader的行为,比如返回表头、数据等
        return mockReader.Object;
    });

或者更简洁的写法:

mockExcelDataMock.Setup(data => data.CreateReader(It.IsAny<MemoryStream>()))
    .Returns(() => new Mock<IExcelDataReader>().Object);

3. 解决「invalid file signature」错误

这个问题的核心是:你测试里的MemoryStream写的是纯文本"sample data",根本不是有效的Excel文件格式,ExcelReaderFactory自然会报错。

单元测试的本质是隔离依赖,咱们完全不需要让代码真的去解析Excel文件,应该把Excel读取的逻辑彻底Mock掉。这里建议你重构一下ExcelData类,把静态依赖的ExcelReaderFactory抽象成接口,方便Mock:

第一步:抽象Excel读取依赖

创建一个接口和实现类,把静态的ExcelReaderFactory包装起来:

// 抽象接口
public interface IExcelReaderFactory
{
    IExcelDataReader CreateReader(Stream stream);
}

// 真实环境的实现
public class ExcelReaderFactoryWrapper : IExcelReaderFactory
{
    public IExcelDataReader CreateReader(Stream stream)
    {
        return ExcelReaderFactory.CreateReader(stream);
    }
}

第二步:修改ExcelData类,依赖注入接口

把原来直接调用静态方法的代码改成依赖注入:

public class ExcelData
{
    private readonly IExcelReaderFactory _excelReaderFactory;

    // 通过构造函数注入依赖
    public ExcelData(IExcelReaderFactory excelReaderFactory)
    {
        _excelReaderFactory = excelReaderFactory;
    }

    // 注意:异步方法要加async和await,否则流可能没写完就被读取了!
    public async Task<DataTable> ReadFromExcel(CloudBlockBlob MasterDataSourceBlob, string SheetName)
    {
        DataTable dataInExcelSheet = new DataTable();
        using (var memoryStream = new MemoryStream())
        {
            await MasterDataSourceBlob.DownloadToStreamAsync(memoryStream);
            memoryStream.Position = 0; // 重置流的位置,否则会从末尾开始读取
            var headers = new List<string>();
            IExcelDataReader excelReader = _excelReaderFactory.CreateReader(memoryStream);
            
            // 这里补充你的Excel解析逻辑,比如转成DataTable
            // 示例:
            // var dataSet = excelReader.AsDataSet(new ExcelDataSetConfiguration
            // {
            //     ConfigureDataTable = _ => new ExcelDataTableConfiguration { UseHeaderRow = true }
            // });
            // dataInExcelSheet = dataSet.Tables[SheetName];
        }
        return dataInExcelSheet;
    }
}

第三步:调整测试代码,完全Mock依赖

现在测试时不需要真实的Excel文件,直接MockIExcelDataReader返回预设数据:

[Fact]
public async Task ReadFromExcel_Success()
{
    // 注册编码解决1252问题
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

    // 1. Mock IExcelDataReader,预设它的行为
    var mockExcelReader = new Mock<IExcelDataReader>();
    // 模拟表头和数据行
    mockExcelReader.Setup(r => r.FieldCount).Returns(2);
    mockExcelReader.SetupSequence(r => r.Read())
        .Returns(true) // 表头行
        .Returns(true) // 第一行数据
        .Returns(false); // 读取结束
    mockExcelReader.Setup(r => r.GetName(0)).Returns("ID");
    mockExcelReader.Setup(r => r.GetName(1)).Returns("Name");
    mockExcelReader.Setup(r => r.GetInt32(0)).Returns(1);
    mockExcelReader.Setup(r => r.GetString(1)).Returns("TestUser");

    // 2. Mock IExcelReaderFactory,返回上面的mockReader
    var mockReaderFactory = new Mock<IExcelReaderFactory>();
    mockReaderFactory.Setup(f => f.CreateReader(It.IsAny<Stream>()))
        .Returns(mockExcelReader.Object);

    // 3. Mock CloudBlockBlob,模拟下载流的行为
    var mockBlob = new Mock<CloudBlockBlob>(new Uri("https://samplestorage.blob.core.windows.net/samplecontainer"));
    mockBlob.Setup(b => b.DownloadToStreamAsync(It.IsAny<Stream>(), CancellationToken.None))
        .Callback((Stream target, CancellationToken _) =>
        {
            // 随便写点内容就行,因为不会真的解析
            var writer = new StreamWriter(target);
            writer.Write("dummy content");
            writer.Flush();
        })
        .Returns(Task.CompletedTask);

    // 4. 执行测试并断言
    var excelData = new ExcelData(mockReaderFactory.Object);
    var result = await excelData.ReadFromExcel(mockBlob.Object, "Sheet1");

    Assert.NotNull(result);
    Assert.Equal(2, result.Columns.Count);
    Assert.Equal("ID", result.Columns[0].ColumnName);
    Assert.Equal(1, result.Rows.Count);
    Assert.Equal(1, result.Rows[0]["ID"]);
}

额外提醒

  • 异步方法一定要用await,否则会出现流未写入完成就被读取的问题,测试方法也要改成async Task。
  • 单元测试要聚焦在你的业务逻辑,尽量不要依赖第三方库的真实功能,Mock掉所有外部依赖才是纯单元测试的正确姿势。

内容的提问来源于stack exchange,提问作者chandra sekhar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:48:43