如何使用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
相关产品推荐
相关产品推荐

