我有以下方法需要测试:
public async Task<SomeClass> GetAsync(string partitionKey, string rowKey)
{
var entities = new List<SomeClass>();
await foreach (var e in _tableClient.QueryAsync<SomeClass>(x => x.PartitionKey == partitionKey && x.RowKey == rowKey))
{
entities.Add(e);
}
return entities.FirstOrDefault();
}
我想设置
_tableClient.QueryAsync()
(在最小起订量中)以便能够根据输入参数返回不同的结果。这对于确保我的单元测试涵盖逻辑非常重要。
我的尝试是:
var thingsToMock = new List<(string PartitionKey, string RowKey, string Value)>() {
("maxCount", "maxCount", "0"),
("maxCount", "xyz", "1000"),
("maxCount", "abc", "2000")
};
var tableClientMock = new Mock<TableClient>();
foreach (var thingToMock in thingsToMock)
{
var returnPage = Page<SomeClass>.FromValues(new List<SomeClass>
{
new SomeClass{ PartitionKey = thingToMock.PartitionKey, RowKey = thingToMock.RowKey, Value = thingToMock.Value }
}, null, new Mock<Response>().Object);
var returnPages = AsyncPageable<SomeClass>.FromPages(new[] { returnPage });
Expression<Func<SomeClass, bool>> exp = (x) => x.PartitionKey == thingToMock.PartitionKey && x.RowKey == thingToMock.RowKey ? true : false;
tableClientMock
.Setup(i => i.QueryAsync<SomeClass>(It.Is<Expression<Func<SomeClass, bool>>>(expression => LambdaExpression.Equals(expression, exp)), null, null, default))
.Returns(returnPages);
}
问题是,当我调用
_tableClientMock
时,GetAsync("maxCount", "abc")
似乎没有返回我所期望的结果。我希望通过这个调用,它会将相同的参数传递给 tableClient.QueryAsync()
方法,在我的模拟中,该方法应该返回值为 2000 的 SomeClass
实例。但是,它会抛出“对象引用未设置为实例”一个物体的”。错误。
如果我将
tableClientMock
的 QueryAsync
设置更改为以下内容,它 有点 有效:
.Setup(i => i.QueryAsync<SomeClass>(It.IsAny<Expression<Func<SomeClass, bool>>>(), null, null, default))
但这并不能达到我的目标,即能够传递不同的参数(
partitionKey
和rowKey
)以获得不同的结果。
我正在使用以下 NuGet 包:
所以我最终进行了以下测试。这与我在问题中提供的示例有点不同。但总的来说,如何模拟 Azure 的概念是相同的
TableClient
单元测试
// Arrange
var expectedItems = new List<ItemEntity>
{
new ItemEntity { RowKey = $"0001", PartitionKey = "abc" },
new ItemEntity { RowKey = $"0002", PartitionKey = "abc" },
}
var tableClient = new Mock<TableClient>();
tableClient
.Setup(c => c.QueryAsync<ItemEntity>(It.IsAny<string>(),
It.Is<int>(a => a == 2),
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()))
.Returns(new MockAsyncPageable<ItemEntity>(expectedItems));
var repo = new Service(tableClient);
// Act
var result = await repo.GetItems("000", 2);
// Assert
result.Should().NotBeNull();
result.Any().Should().BeTrue();
result.Count().Should().Be(2);
tableClient.Verify(c => c.QueryAsync<ItemEntity>(It.IsAny<string>(),
It.Is<int>(a => a == 2),
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()), Times.Once);
服务的
GetItems
方法如下所示:
public async Task<IEnumerable<ItemEntity>> GetItems(string code, int maxPerPage)
{
var maxPerPage = Math.Clamp(maxPerPage, 1, 1000);
var filter = $"(RowKey eq '{partialNdc}') and (PartitionKey eq 'abc')";
return await _tableClient.QueryAsync<ItemEntity>(filter, maxPerPage);
}
最后是,
MockAsyncPageable<>
internal class MockAsyncPageable<T> : AsyncPageable<T>
{
private readonly List<T> _items;
public MockAsyncPageable(List<T> items)
{
_items = items;
}
public override IAsyncEnumerable<Page<T>> AsPages(string continuationToken = null, int? pageSizeHint = null)
{
var page = Page<T>.FromValues(_items, null, new Mock<Response>().Object);
return new[] { page }.ToAsyncEnumerable();
}
}