如何正确Mock Azure Storage CloudTable的CreateQuery方法
问题原因
你当前代码抛出空引用主要由两个问题导致:
- 构造
CloudTableMock实例时,第二个参数传入了null,该位置对应存储凭据参数,SDK内部初始化逻辑会访问该对象的属性,直接传null会触发空引用。 - 仅Mock了
CreateQuery<TableEntity>()方法,返回了一个默认new出来的空TableQuery<TableEntity>实例,该实例没有绑定查询执行上下文,内部IQueryable相关的Provider属性未初始化,后续链式调用Where()、ToList()时访问到null值就会抛出异常。
正确Mock方案
单元测试不需要验证Cosmos Table SDK本身的内部逻辑,只需要模拟查询最终返回的结果即可,不需要逐段Mock整个链式调用链路,实现步骤如下:
- 先准备好测试用的预期返回实体集合,转成内存IQueryable对象
- Mock
TableQuery<TableEntity>实例,将其IQueryable接口的核心成员映射到内存集合的对应成员,让查询直接在内存执行 - 构造
CloudTableMock时传入空的存储凭据实例替代null,SetupCreateQuery方法返回前面Mock好的TableQuery对象
完整代码示例:
using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Auth; using Microsoft.WindowsAzure.Storage.Table; using Moq; using System.Collections.Generic; using System.Linq; // 1. 准备测试数据 var expectedEntities = new List<TableEntity> { new TableEntity("testPartition", "row1"), new TableEntity("testPartition", "row2") }.AsQueryable(); // 2. Mock TableQuery,映射IQueryable逻辑到内存集合 var tableQueryMock = new Mock<TableQuery<TableEntity>>(); tableQueryMock.As<IQueryable<TableEntity>>() .Setup(q => q.Provider) .Returns(expectedEntities.Provider); tableQueryMock.As<IQueryable<TableEntity>>() .Setup(q => q.Expression) .Returns(expectedEntities.Expression); tableQueryMock.As<IQueryable<TableEntity>>() .Setup(q => q.ElementType) .Returns(expectedEntities.ElementType); tableQueryMock.As<IQueryable<TableEntity>>() .Setup(q => q.GetEnumerator()) .Returns(expectedEntities.GetEnumerator()); // 3. Mock CloudTable,注意第二个参数传空StorageCredentials不要传null var cloudTableMock = new Mock<CloudTable>( new Uri("http://unittests.localhost.com/FakeTable"), new StorageCredentials() ); cloudTableMock.Setup(t => t.CreateQuery<TableEntity>()) .Returns(tableQueryMock.Object);
注意事项
Where()、ToList()大多是查询扩展方法,Moq无法直接Mock扩展方法,逐段模拟链式调用会让测试代码非常脆弱,不推荐这么做。- 上述方案中查询谓词
predicate会在内存集合上正常执行,返回符合条件的结果,完全满足单元测试的验证需求,不会触发真实的服务请求。
内容的提问来源于stack exchange,提问作者Sainu Thaha
相关产品推荐
相关产品推荐

