如何Mock GraphServiceClient测试AzureAdAccessManager的CreateUser方法?
如何正确Mock GraphServiceClient测试AzureAdAccessManager的CreateUser方法?
我正在为AzureAdAccessManager类编写单元测试,该类通过GraphServiceClient与Microsoft Graph API交互。以下是相关代码及遇到的错误:
AzureAdAccessManager实现代码
public AzureAdAccessManager(IConfiguration configuration, ILog logger, IAccessTokenService accessTokenService, IOptions<ConfidentialClientApplicationOptions> options) { _configuration = configuration; _logger = logger; _accessTokenService = accessTokenService; _option = options; } public virtual Task<GraphServiceClient> GetGraphClient() { var clientId = _option.Value.ClientId; var tenantId = _option.Value.TenantId; var clientSecret = _option.Value.ClientSecret; var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret); var graphServiceClient = new GraphServiceClient(clientSecretCredential, new[] { "https://graph.microsoft.com/.default" }); return Task.FromResult(graphServiceClient); } public async Task<User> CreateUser(User user) { _logger.LogInfo("AzureAdAccessManager : Starting method CreateUser."); var graphClient = await GetGraphClient(); return await graphClient.Users.PostAsync(user); }
测试用例实现代码
protected override Mock<GraphServiceClient> OnBuild() { var graphServiceClientMock = new Mock<GraphServiceClient>(); return graphServiceClientMock; }
错误信息
System.NotSupportedException: Unsupported expression: ... => ....PostAsync(It.IsAny(), null, CancellationToken)
Non-overridable members (here: UsersRequestBuilder.PostAsync) may not be used in setup/verification expressions.
问题原因
报错核心原因是GraphServiceClient.Users.PostAsync属于非虚方法,Moq框架仅支持Mock虚方法或接口成员,无法直接对非虚方法进行Mock设置。
解决方法
方法1:依赖抽象接口(推荐)
Microsoft Graph SDK提供了IGraphServiceClient抽象接口,GraphServiceClient是该接口的实现类。通过依赖抽象而非具体类,既能符合依赖倒置原则,也能让Mock更简单:
- 修改
GetGraphClient的返回类型为抽象接口:
public virtual Task<IGraphServiceClient> GetGraphClient() { var clientId = _option.Value.ClientId; var tenantId = _option.Value.TenantId; var clientSecret = _option.Value.ClientSecret; var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret); var graphServiceClient = new GraphServiceClient(clientSecretCredential, new[] { "https://graph.microsoft.com/.default" }); return Task.FromResult<IGraphServiceClient>(graphServiceClient); }
- 同步修改
CreateUser方法中客户端的类型:
public async Task<User> CreateUser(User user) { _logger.LogInfo("AzureAdAccessManager : Starting method CreateUser."); IGraphServiceClient graphClient = await GetGraphClient(); return await graphClient.Users.PostAsync(user); }
- 测试时Mock
IGraphServiceClient及请求链:
protected override Mock<IGraphServiceClient> OnBuild() { // Mock 用户请求构建器 var usersRequestBuilderMock = new Mock<IUsersRequestBuilder>(); usersRequestBuilderMock .Setup(x => x.PostAsync(It.IsAny<User>(), null, default)) .ReturnsAsync(new User { Id = "test-user-id", DisplayName = "Test User" }); // Mock Graph服务客户端 var graphServiceClientMock = new Mock<IGraphServiceClient>(); graphServiceClientMock.Setup(x => x.Users).Returns(usersRequestBuilderMock.Object); return graphServiceClientMock; }
方法2:Mock完整请求链(不修改原接口时使用)
如果不想改动原代码的返回类型,需要逐层MockGraphServiceClient的属性和请求构建器:
protected override Mock<GraphServiceClient> OnBuild() { // Mock UsersRequestBuilder var usersRequestBuilderMock = new Mock<UsersRequestBuilder>(string.Empty, null); usersRequestBuilderMock .Setup(x => x.PostAsync(It.IsAny<User>(), null, default)) .ReturnsAsync(new User { Id = "test-user-id", DisplayName = "Test User" }); // Mock GraphServiceClient,用SetupGet处理属性 var graphServiceClientMock = new Mock<GraphServiceClient>(); graphServiceClientMock.SetupGet(x => x.Users).Returns(usersRequestBuilderMock.Object); return graphServiceClientMock; }
关键注意点
- Moq不支持Mock非虚方法、密封类成员,必须针对抽象接口或可继承的虚成员操作。
- 依赖抽象接口的方式更利于代码扩展和测试维护,是长期优化的首选方案。
内容的提问来源于stack exchange,提问作者Abhishek Saxena
相关产品推荐
相关产品推荐

