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

如何使用FakeItEasy伪造返回AsyncPageable的Azure表查询结果?

如何伪造Azure Tables查询返回的AsyncPageable结果用于测试?

我在测试基于Azure Tables的Azure Function时,遇到了伪造AsyncPageable<T>类型查询结果的问题,以下是我的代码和尝试:

被测代码

public async Task<Project?> GetProjectByIdAsync(Guid projectId)
{
    const int maxResultsPerPage = 1;
    IEnumerable<string>? selectProps = null;

    // _projectsTableService 是 ITableService 类型
    var tableClient = _projectsTableService.GetTableClient();

    var results = tableClient.QueryAsync<Project>(project => project.Id.Equals(projectId),
                                                  maxResultsPerPage,
                                                  selectProps,
                                                  CancellationToken.None);

    await foreach (var result in results)
    {
        return result;
    }

    return null;
}

测试初始化代码

[TestInitialize]
public void TestInit()
{
    _project = new Project { Id = Guid.NewGuid() };
    _tableClient = A.Fake<TableClient>();
    _tableService = A.Fake<ITableService>();
    _tableServiceFactory = A.Fake<ITableServiceFactory>();

    A.CallTo(() => _tableService.GetTableClient()).Returns(_tableClient);
    A.CallTo(() => _tableServiceFactory.Create(TableName)).Returns(_tableService);

    _testee = new ProjectsTableService(_tableServiceFactory);
}

第一次尝试(无效)

调试时发现results这个AsyncPageable<Project>没有包含任何结果:

[TestMethod]
public async Task GetProjectByIdAsync_WhenMatchingProjectFound_ReturnsProject()
{
    var projectId = Guid.NewGuid();
    var project = new Project { Id = projectId };
    var projectsResult = new List<Project> { _project };
    var asyncPageable = A.Fake<AsyncPageable<Project>>();
    var enumerator = projectsResult.ToAsyncEnumerable().GetAsyncEnumerator();

    A.CallTo(() => asyncPageable.GetAsyncEnumerator(A<CancellationToken>._)).Returns(enumerator);

    A.CallTo(() => _tableClient.QueryAsync(A<Expression<Func<Project, bool>>>._,
                                           A<int>._,
                                           A<IEnumerable<string>>._,
                                           A<CancellationToken>._)).Returns(asyncPageable);

    var result = await _testee.GetProjectByIdAsync(_project.Id);

    result.Should().NotBeNull();
    result.Id.Should().Be(projectId);
}

第二次尝试(无效)

调整后仍未解决问题:

[TestMethod]
public async Task GetProjectByIdAsync_WhenMatchingProjectFound_ReturnsProject2()
{
    var projectId = Guid.NewGuid();
    var project = new Project { Id = projectId };
    var projects = new List<Project> { _project };
    var page = Page<Project>.FromValues(projects,
                                        continuationToken: null,
                                        response: A.Fake<Response>());
    var pages = AsyncPageable<Project>.FromPages(new[] { page });

    A.CallTo(() => _tableClient.QueryAsync(A<Expression<Func<Project, bool>>>._,
                                           A<int>._,
                                           A<IEnumerable<string>>._,
                                           A<CancellationToken>._)).Returns(pages);

    var result = await _testee.GetProjectByIdAsync(_project.Id);

    result.Should().NotBeNull();
    result.Id.Should().Be(projectId);
}

解决方案

方法1:使用AsyncPageable.FromEnumerable简化创建

不需要伪造AsyncPageable,直接用Azure提供的静态方法从普通集合创建,这是最简洁的方案:

[TestMethod]
public async Task GetProjectByIdAsync_WhenMatchingProjectFound_ReturnsProject()
{
    var targetProjectId = _project.Id;
    var asyncPageable = AsyncPageable<Project>.FromEnumerable(new[] { _project });

    A.CallTo(() => _tableClient.QueryAsync(A<Expression<Func<Project, bool>>>._,
                                           A<int>._,
                                           A<IEnumerable<string>>._,
                                           A<CancellationToken>._))
     .Returns(asyncPageable);

    var result = await _testee.GetProjectByIdAsync(targetProjectId);

    result.Should().NotBeNull();
    result.Id.Should().Be(targetProjectId);
}

FromEnumerable会自动处理异步枚举的所有细节,无需手动创建分页或枚举器。

方法2:修复第二次尝试的逻辑错误

第二次尝试的核心问题是数据不匹配:测试中创建了新的project(新Guid),但查询用的是_project.Id,断言却指向新Guid。调整后对齐数据即可:

[TestMethod]
public async Task GetProjectByIdAsync_WhenMatchingProjectFound_ReturnsProject2()
{
    var targetProjectId = _project.Id;
    var page = Page<Project>.FromValues(new[] { _project },
                                        continuationToken: null,
                                        response: A.Fake<Response>());
    var pages = AsyncPageable<Project>.FromPages(new[] { page });

    A.CallTo(() => _tableClient.QueryAsync(A<Expression<Func<Project, bool>>>._,
                                           A<int>._,
                                           A<IEnumerable<string>>._,
                                           A<CancellationToken>._))
     .Returns(pages);

    var result = await _testee.GetProjectByIdAsync(targetProjectId);

    result.Should().NotBeNull();
    result.Id.Should().Be(targetProjectId);
}

第一次尝试失败的原因

AsyncPageable的异步枚举依赖内部分页机制,直接伪造GetAsyncEnumerator无法模拟其完整行为,await foreach会触发分页加载逻辑,而非直接使用你提供的枚举器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 07:15:01