You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何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更简单:

  1. 修改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);
}
  1. 同步修改CreateUser方法中客户端的类型:
public async Task<User> CreateUser(User user)
{
    _logger.LogInfo("AzureAdAccessManager : Starting method CreateUser.");
    IGraphServiceClient graphClient = await GetGraphClient();
    return await graphClient.Users.PostAsync(user);
}
  1. 测试时MockIGraphServiceClient及请求链:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 03:50:14