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

如何对调用CreateQuery<DynamicTableEntity>的方法进行单元测试

解决Azure Table Storage单元测试中Mock CreateQuery引发的空引用异常

问题背景

生产代码如下:

public async Task<List<MyEntity>> GetEntities()
{
    var query = this.table.CreateQuery<DynamicTableEntity>()
           .Where(d =>  d.Properties[nameof(MyEntity.Fid)].GuidValue == Guid.Empty)
           .AsTableQuery();

    var result = table.ExecuteQuery<DynamicTableEntity>(query);

    // 其他逻辑
}

单元测试时,this.table.CreateQuery().Where行触发System.NullReferenceException。尝试Mock CreateQuery返回空TableQuery:

cloudTable.Setup(s => s.CreateQuery<DynamicTableEntity>()).Returns(new TableQuery<DynamicTableEntity>());

编译通过但运行时仍抛出空引用异常,推测是Where操作访问的集合相关属性为null。

问题原因

手动创建的TableQuery<DynamicTableEntity>实例未初始化IQueryable接口所需的Provider属性,而Queryable.Where扩展方法会访问该属性,导致空引用。

解决方案

核心思路是:Mock CreateQuery返回一个具备有效IQueryable属性的TableQuery实例,同时Mock ExecuteQuery返回预设测试数据(实际业务逻辑依赖的是ExecuteQuery的结果,而非CreateQuery构建的查询对象)。

步骤1:准备测试数据

先构造符合业务场景的DynamicTableEntity列表:

// 构造测试用的DynamicTableEntity集合
var testDynamicEntities = new List<DynamicTableEntity>
{
    new DynamicTableEntity
    {
        PartitionKey = "test-partition",
        RowKey = "test-row-1",
        Properties = new Dictionary<string, EntityProperty>
        {
            { nameof(MyEntity.Fid), new EntityProperty(Guid.Empty) }
        }
    },
    // 可添加更多测试实体
}.AsQueryable();

步骤2:Mock TableQuery实例

通过Moq模拟TableQuery<DynamicTableEntity>,并实现其IQueryable接口的必要属性,避免空引用:

var mockTableQuery = new Mock<TableQuery<DynamicTableEntity>>();

// 实现IQueryable接口的核心属性
mockTableQuery.As<IQueryable<DynamicTableEntity>>()
              .Setup(q => q.Provider)
              .Returns(testDynamicEntities.Provider);

mockTableQuery.As<IQueryable<DynamicTableEntity>>()
              .Setup(q => q.Expression)
              .Returns(testDynamicEntities.Expression);

mockTableQuery.As<IQueryable<DynamicTableEntity>>()
              .Setup(q => q.ElementType)
              .Returns(testDynamicEntities.ElementType);

mockTableQuery.As<IQueryable<DynamicTableEntity>>()
              .Setup(q => q.GetEnumerator())
              .Returns(testDynamicEntities.GetEnumerator());

步骤3:Mock CloudTable的方法

将Mock好的TableQuery关联到CloudTable的CreateQuery方法,同时Mock ExecuteQuery返回预设数据:

var mockCloudTable = new Mock<CloudTable>(new Uri("http://localhost/test-table"));

// Mock CreateQuery返回具备有效IQueryable属性的TableQuery
mockCloudTable.Setup(s => s.CreateQuery<DynamicTableEntity>())
              .Returns(mockTableQuery.Object);

// Mock ExecuteQuery返回预设的测试数据
mockCloudTable.Setup(s => s.ExecuteQuery<DynamicTableEntity>(It.IsAny<TableQuery<DynamicTableEntity>>()))
              .Returns(testDynamicEntities.ToList());

简化方案(忽略查询构建细节)

如果不需要验证查询条件的正确性,可直接Mock ExecuteQuery返回数据,同时让CreateQuery返回一个不会引发空引用的Mock实例:

var mockCloudTable = new Mock<CloudTable>(new Uri("http://localhost/test-table"));

// 返回Mock的TableQuery避免空引用
mockCloudTable.Setup(s => s.CreateQuery<DynamicTableEntity>())
              .Returns(new Mock<TableQuery<DynamicTableEntity>>().Object);

// 直接返回预设数据
mockCloudTable.Setup(s => s.ExecuteQuery<DynamicTableEntity>(It.IsAny<TableQuery<DynamicTableEntity>>()))
              .Returns(testDynamicEntities.ToList());

关键说明

  • TableQuery本质是查询定义对象,而非数据集合,业务逻辑实际依赖的是ExecuteQuery返回的结果,因此Mock ExecuteQuery是核心。
  • 手动创建的TableQuery因缺少IQueryable的Provider属性,调用Where等扩展方法时会触发空引用,需通过Mock补全必要属性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 18:35:09