如何在单元测试中Mock GraphServiceClient并返回模拟响应?
解决GraphServiceClient单元测试Mock失败问题
问题场景
业务代码依赖GraphServiceClient获取域名注册商信息:
private GraphServiceClient gsc; public RiskIQService(GraphServiceClient gsc) { // 注意:原代码存在bug,未给私有字段赋值,需改为 this.gsc = gsc; gsc = gsc; } public async Task<string> getRegistrar(string domain) { Microsoft.Graph.Models.Security.Hostname? response = (Microsoft.Graph.Models.Security.Hostname?) await gsc.Security.ThreatIntelligence.Hosts[domain].GetAsync(); return response.Registrar; }
尝试直接Mock GraphServiceClient时触发错误:
System.NotSupportedException: Unsupported expression: ... => ....GetAsync(null, CancellationToken.None) Non-overridable members (here: HostItemRequestBuilder.GetAsync) may not be used in setup / verification expressions.
原Mock代码:
private readonly Mock<GraphServiceClient> gscMock = new Mock<GraphServiceClient>(); var response = new Hostname(Registrar = "Registrar"); gscMock.Setup(x => x.Security.ThreatIntelligence.Hosts[It.IsAny<string>()].GetAsync(null, CancellationToken.None)).ReturnsAsync(response);
错误原因
Microsoft Graph SDK中的HostItemRequestBuilder.GetAsync是非虚方法,Moq仅支持Mock虚方法或接口定义的方法,直接对GraphServiceClient类进行链式Setup会触发非虚方法不支持的错误。
正确Mock方案
利用Graph SDK提供的各层级请求构建器接口,逐层Mock每个依赖节点:
// 1. Mock最底层的Host请求构建器,模拟GetAsync返回结果 var hostItemMock = new Mock<IHostItemRequestBuilder>(); var mockHostname = new Microsoft.Graph.Models.Security.Hostname { Registrar = "TestRegistrar" }; hostItemMock.Setup(x => x.GetAsync(It.IsAny<RequestConfiguration<DefaultQuerySettings>>(), It.IsAny<CancellationToken>())) .ReturnsAsync(mockHostname); // 2. Mock Hosts集合构建器,索引器返回上面的Host请求构建器 var hostsCollectionMock = new Mock<IHostsCollectionRequestBuilder>(); hostsCollectionMock.Setup(x => x[It.IsAny<string>()]) .Returns(hostItemMock.Object); // 3. Mock ThreatIntelligence构建器,返回Hosts集合构建器 var threatIntelligenceMock = new Mock<IThreatIntelligenceRequestBuilder>(); threatIntelligenceMock.Setup(x => x.Hosts) .Returns(hostsCollectionMock.Object); // 4. Mock Security构建器,返回ThreatIntelligence构建器 var securityMock = new Mock<ISecurityRequestBuilder>(); securityMock.Setup(x => x.ThreatIntelligence) .Returns(threatIntelligenceMock.Object); // 5. 最后Mock GraphServiceClient,返回Security构建器 var gscMock = new Mock<GraphServiceClient>(); gscMock.Setup(x => x.Security) .Returns(securityMock.Object); // 注入Mock对象到业务服务 var riskIQService = new RiskIQService(gscMock.Object); // 执行测试 var result = await riskIQService.getRegistrar("test-domain.com"); // 断言结果 Assert.Equal("TestRegistrar", result);
额外注意
- 原业务代码的构造函数存在赋值错误,必须将
gsc = gsc;改为this.gsc = gsc;,否则私有字段gsc始终为null,无法正常执行。 - 若使用的Graph SDK版本不同,接口名称可能略有差异,请以实际SDK定义为准。
内容的提问来源于stack exchange,提问作者Punter Vicky
相关产品推荐
相关产品推荐

