C#使用Moq与xUnit单元测试时如何Mock GraphServiceClient调用
基于Moq Mock GraphServiceClient 调用的实现方案
前置重构要求
首先你需要调整原有业务代码的写法,不要直接在业务逻辑内实例化GraphServiceClient,改为通过依赖注入注入接口IGraphServiceClient,才可以完成Mock替换:
public class YourBusinessService { private readonly IGraphServiceClient _graphClient; // 构造函数注入IGraphServiceClient,由DI容器完成初始化 public YourBusinessService(IGraphServiceClient graphClient) { _graphClient = graphClient; } public async Task<User> GetUserByMobilePhoneAsync(string mobilePhone) { return (await _graphClient.Users.Request().Filter($"displayName eq '{mobilePhone}'").GetAsync()).FirstOrDefault(); } }
单元测试实现逻辑
由于Graph SDK的调用是链式结构,需要逐层配置Mock返回值,请求链对应接口层级为:IGraphServiceClient → IGraphUsersRequestBuilder → IGraphUsersCollectionRequest,完整xUnit测试代码示例如下:
using Moq; using Xunit; using Microsoft.Graph; public class YourBusinessServiceTests { [Fact] public async Task GetUserByMobilePhoneAsync_ReturnsCorrectUser_WhenUserExists() { // 1. 准备测试数据 var testMobilePhone = "13800138000"; var expectedUser = new User { DisplayName = testMobilePhone, Id = "test-user-id" }; var mockUserPage = new GraphServiceUsersCollectionPage { new User { DisplayName = "other-user" }, expectedUser }; // 2. 逐层配置Mock链 // Mock 集合请求实例 var mockUsersRequest = new Mock<IGraphUsersCollectionRequest>(); // 配置Filter方法:匹配过滤条件后返回请求自身,不需要校验参数时可替换为It.IsAny<string>() mockUsersRequest.Setup(r => r.Filter(It.Is<string>(f => f == $"displayName eq '{testMobilePhone}'"))) .Returns(mockUsersRequest.Object); // 配置GetAsync方法返回模拟的用户集合 mockUsersRequest.Setup(r => r.GetAsync()) .ReturnsAsync(mockUserPage); // Mock 用户请求构建器 var mockUsersBuilder = new Mock<IGraphUsersRequestBuilder>(); mockUsersBuilder.Setup(b => b.Request()) .Returns(mockUsersRequest.Object); // Mock Graph客户端实例 var mockGraphClient = new Mock<IGraphServiceClient>(); mockGraphClient.Setup(c => c.Users) .Returns(mockUsersBuilder.Object); // 3. 注入Mock实例执行业务逻辑 var businessService = new YourBusinessService(mockGraphClient.Object); var result = await businessService.GetUserByMobilePhoneAsync(testMobilePhone); // 4. 断言结果 Assert.NotNull(result); Assert.Equal(expectedUser.Id, result.Id); // 可选:验证Filter确实传入了正确的参数 mockUsersRequest.Verify(r => r.Filter(It.Is<string>(f => f == $"displayName eq '{testMobilePhone}'")), Times.Once); } }
注意事项
- 如果不需要校验
Filter的传参是否正确,可以把Filter的配置参数改为It.IsAny<string>(),适配任意过滤条件 - 如果你使用的是v5及以上版本的Microsoft Graph SDK,请求写法有调整,对应的Mock接口也需要同步更换为新版的接口定义
- 不要直接Mock
GraphServiceClient实现类,始终Mock其暴露的IGraphServiceClient接口,可以避免很多内部实现导致的Mock失败问题
内容的提问来源于stack exchange,提问作者Davide Boldrin
相关产品推荐
相关产品推荐

