c# – 如何正确编写异步XUnit测试?
我正在使用异步xUnit测试,我注意到不一致的传递行为:
public async Task FetchData() { //Arrange var result = await arrangedService.FetchDataAsync().ConfigureAwait(false); //Assert } 我已经完成了此测试执行的调用堆栈,并验证了我的所有库代码都在每个任务之后调用.ConfigureAwait(false).但是,尽管如此,在执行全部运行时,此测试和其他测试将间歇性地失败,但是当我在调试器上完成时,会通过断言和手动检查.很明显,我没有正确地做某事.我已经尝试在测试本身中删除对ConfigureAwait(false)的调用,以防有特殊的xUnit同步上下文,但它没有改变任何东西.以一致的方式测试异步代码的最佳方法是什么? 编辑好的,这是我尝试创建一个运行的代码的超简化示例,以提供正在发生的事情的示例: using Graph = Microsoft.Azure.ActiveDirectory.GraphClient; public async Task FetchData() { var adUsers = baseUsers //IEnumerable<Graph.User> .Cast<Graph.IUser>() .ToList(); var nextPageUsers = Enumerable .Range(GoodIdMin,GoodIdMax) .Select(number => new Graph.User { Mail = (-number).ToString() }) .Cast<Graph.IUser>() .ToList(); var mockUserPages = new Mock<IPagedCollection<Graph.IUser>>(); mockUserPages .Setup(pages => pages.MorePagesAvailable) .Returns(true); mockUserPages .Setup(pages => pages.CurrentPage) .Returns(new ReadOnlyCollection<Graph.IUser>(adUsers)); mockUserPages .Setup(pages => pages.GetNextPageAsync()) .ReturnsAsync(mockUserPages.Object) .Callback(() => { mockUserPages .Setup(pages => pages.CurrentPage) .Returns(new ReadOnlyCollection<Graph.IUser>(nextPageUsers)); mockUserPages .Setup(pages => pages.MorePagesAvailable) .Returns(false); }); var mockUsers = new Mock<Graph.IUserCollection>(); mockUsers .Setup(src => src.ExecuteAsync()) .ReturnsAsync(mockUserPages.Object); var mockGraphClient = new Mock<Graph.IActiveDirectoryClient>(); mockGraphClient .Setup(src => src.Users) .Returns(mockUsers.Object); var mockDbUsers = CreateBasicMockDbSet(baseUsers.Take(10) .Select(user => new User { Mail = user.Mail }) .AsQueryable()); var mockContext = new Mock<MyDbContext>(); mockContext .Setup(context => context.Set<User>()) .Returns(mockDbUsers.Object); var mockGraphProvider = new Mock<IGraphProvider>(); mockGraphProvider .Setup(src => src.GetClient()) //Creates an IActiveDirectoryClient .Returns(mockGraphClient.Object); var getter = new UserGetter(mockContext.Object,mockGraphProvider.Object); var result = await getter.GetData().ConfigureAwait(false); Assert.True(result.Success); //Not the actual assert } 这是在var result = … line上执行的代码: public UserGetterResult GetData() { var adUsers = await GetAdUsers().ConfigureAwait(false); var dbUsers = Context.Set<User>().ToList(); //This is the injected context from before return new UserGetterResult //Just a POCO { AdUsers = adUsers .Except(/*Expression that indicates whether or not this user is in the database*/) .ProjectTo<User>() .ToList(),DbUsers = dbUsers.ProjectTo<User>().ToList() //Automapper 6.1.1 }; } private async Task<List<User>> GetAdUsers() { var userPages = await client //Injected IActiveDirectoryClient from before .Users .ExecuteAsync() .ConfigureAwait(false); var users = userPages.CurrentPage.ToList(); while(userPages.MorePagesAvailable) { userPages = await userPages.GetNextPageAsync().ConfigureAwait(false); users.AddRange(userPages.CurrentPage); } return users; } 代码的目的是获取AD中但不是数据库的用户列表以及数据库中的用户列表. 编辑编辑因为我忘了在原始更新中包含它,所以在调用`IUserCollection.ExecuteAsync()时都会发生错误. 解决方法
IUserCollection.ExecuteAsync()似乎是根据原始帖子中显示的内容正确配置的.
现在关注以下方法…… private async Task<List<User>> GetAdUsers() { var userPages = await client //Injected IActiveDirectoryClient from before .Users .ExecuteAsync() .ConfigureAwait(false); var users = userPages.CurrentPage.ToList(); while(userPages.MorePagesAvailable) { userPages = await userPages.GetNextPageAsync().ConfigureAwait(false); users.AddRange(userPages.CurrentPage); } return users; } 我关心如何在模拟中设置用户页面.鉴于GetAdUsers方法的流程,最好使用SetupSequence来模拟重复调用CurrentPage和MorePagesAvailable. var mockUserPages = new Mock<IPagedCollection<Graph.IUser>>(); mockUserPages .SetupSequence(_ => _.MorePagesAvailable) .Returns(true) // First time called to enter while loop .Returns(false); // Second time called to exit while loop mockUserPages .SetupSequence(_ => _.CurrentPage) .Returns(new ReadOnlyCollection<Graph.IUser>(adUsers)) // First time called to get List .Returns(new ReadOnlyCollection<Graph.IUser>(nextPageUsers)); // Second time called to get next page mockUserPages .Setup(pages => pages.GetNextPageAsync()) .ReturnsAsync(mockUserPages.Object); // No need for callback 参考Moq Quickstart (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |