Mock和单元测试Microsoft Graph API获取用户列表:空引用错误与Setup用法
问题分析与解决方法
现有测试代码的问题
- 空引用错误原因:
mockGraphClient.Setup(g => g.Users.Request())中,g.Users未被Mock,直接访问会返回null,触发空引用异常。 - 类型不匹配:
ReturnsAsync(users)返回单个User对象,但GetAsync()实际返回的是IGraphServiceUsersCollectionPage类型,类型完全不兼容。 - 测试对象偏差:测试中直接调用
graphClient.Users.Request().GetAsync(),而非你封装的业务方法GetUsersAsync(),没有覆盖实际业务逻辑的测试场景。
修正后的测试实现步骤
1. 构造模拟的用户集合页
需要创建IGraphServiceUsersCollectionPage的Mock实例,用来承载测试用的用户列表:
// 构造测试用的用户数据 var testUsers = new List<User> { new User { Id = "1", DisplayName = "test", GivenName = "test2", Surname = "test3" }, new User { Id = "2", DisplayName = "Alice", GivenName = "Alice", Surname = "Smith" } }; // Mock集合页对象,返回测试用户列表 var mockUsersPage = new Mock<IGraphServiceUsersCollectionPage>(); mockUsersPage.Setup(p => p.CurrentPage).Returns(testUsers);
2. 分层Mock GraphClient的链式调用
Graph SDK的调用是链式结构,需要逐层Mock每个节点,避免空引用:
var mockAuthProvider = new Mock<IAuthenticationProvider>(); var mockHttpProvider = new Mock<IHttpProvider>(); var mockGraphClient = new Mock<GraphServiceClient>(mockAuthProvider.Object, mockHttpProvider.Object); // 1. Mock Users属性,返回集合请求构建器 var mockUsersCollectionBuilder = new Mock<IGraphServiceUsersCollectionRequestBuilder>(); mockGraphClient.Setup(g => g.Users).Returns(mockUsersCollectionBuilder.Object); // 2. Mock Request()方法,返回集合请求对象 var mockUsersRequest = new Mock<IGraphServiceUsersCollectionRequest>(); mockUsersCollectionBuilder.Setup(u => u.Request(It.IsAny<IEnumerable<Option>>())) .Returns(mockUsersRequest.Object); // 3. Mock Select/OrderBy方法,返回自身以维持链式调用 mockUsersRequest.Setup(r => r.Select(It.IsAny<string>())) .Returns(mockUsersRequest.Object); mockUsersRequest.Setup(r => r.OrderBy(It.IsAny<string>())) .Returns(mockUsersRequest.Object); // 4. Mock GetAsync(),返回预先构造的模拟集合页 mockUsersRequest.Setup(r => r.GetAsync(CancellationToken.None)) .ReturnsAsync(mockUsersPage.Object);
3. 测试你的业务方法
假设你的业务类名为UserService,注入GraphServiceClient,测试时传入Mock实例:
[TestMethod] public async Task GetUsersAsync_Success_ReturnsExpectedUsers() { // 实例化业务类,传入Mock的GraphClient var userService = new UserService(mockGraphClient.Object); // Act:调用业务方法 var result = await userService.GetUsersAsync(); // Assert:验证返回结果 Assert.AreEqual(2, result.CurrentPage.Count()); var firstUser = result.CurrentPage.First(); Assert.AreEqual("1", firstUser.Id); Assert.AreEqual("test", firstUser.DisplayName); // 验证链式调用是否符合预期 mockUsersRequest.Verify(r => r.Select(It.Is<string>(s => s.Contains("Id") && s.Contains("DisplayName"))), Times.Once); mockUsersRequest.Verify(r => r.OrderBy("DisplayName"), Times.Once); mockUsersRequest.Verify(r => r.GetAsync(CancellationToken.None), Times.Once); }
关键注意点
- 链式调用Mock规则:Graph SDK的每个链式方法(如Select/OrderBy)都需要Mock返回自身,否则链式调用会中断并触发空引用。
- 严格类型匹配:必须确保返回值是
IGraphServiceUsersCollectionPage类型,不能用单个User对象替代。 - 聚焦业务逻辑:测试目标应该是你封装的业务方法,而非直接测试GraphClient,这样才能验证业务代码的正确性。
内容的提问来源于stack exchange,提问作者Tim Whatley
相关产品推荐
相关产品推荐

