模拟`TableClient.QueryAsync()`进行单元测试

问题描述 投票:0回答:1

我有以下方法需要测试:

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.Data.Tables”版本=“12.7.1”
  • “起订量”版本=“4.14.1”
azure unit-testing .net-core moq azure-tableclient
1个回答
0
投票

所以我最终进行了以下测试。这与我在问题中提供的示例有点不同。但总的来说,如何模拟 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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.