【问题标题】:How to Mock GetDiscoveryDocumentAsync When Unit Testing HttpClient?单元测试 HttpClient 时如何模拟 GetDiscoveryDocumentAsync?
【发布时间】:2020-09-19 15:46:26
【问题描述】:

我正在尝试为 HttpClient 服务编写一些单元测试,但在尝试模拟 GetAccessToken() 函数中的部分代码时遇到了问题。

该服务接受一个 IHttpClientFactory。从我到目前为止所做的阅读看来,要正确地模拟这个,我必须模拟 IHttpClientFactory,让它返回一个依赖于模拟的 HttpMessageHandler 的新 HttpClient,我已经这样做了:

var mockHttpClientFactory = new Mock<IHttpClientFactory>();

var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler.Protected()
    .Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
    .ReturnsAsync(new HttpResponseMessage
        {
         StatusCode = HttpStatusCode.OK,
         Content = new StringContent("{'name':thecodebuzz,'city':'USA'}"),
        });

var client = new HttpClient(mockHttpMessageHandler.Object);
mockHttpClientFactory.Setup(x => x.CreateClient(It.IsAny<string>())).Returns(client);

_httpClientService = new HttpClientService(mockHttpClientFactory.Object, mockConfig.Object); 

这方面工作,我已经能够调用 CreateClient() 工作正常。但是,我尝试测试的函数调用了我的 GetAccessToken() 函数,该函数又调用了 client.GetDiscoveryAccessToken()。我猜 GetDiscoveryAccessToken() 函数需要被模拟,但我不确定如何继续。

我尝试使用模拟方法创建客户端模拟,并在调用 CreateClient 时返回它,如下所示:

 var discoDoc = // Mocked disco doc code;

 var client = new Mock<HttpClient>(mockHttpMessageHandler.Object);
 client.Setup(x => x.GetDiscoveryDocumentAsync(It.IsAny<string>())).Returns(discoDoc);

 mockHttpClientFactory.Setup(x => x.CreateClient(It.IsAny<string>())).Returns(client);

 _httpClientService = new HttpClientService(mockHttpClientFactory.Object, mockConfig.Object);

不幸的是,我在告诉 CreateClient 函数要返回什么的行上收到此错误:“无法从 'Moq.Mock' 转换为 'System.Net.Http.HttpClient'”。我猜这是因为我需要返回一个真正的 HttpClient 而不是一个 Mocked 。

关于我可以从这里去哪里有什么想法吗?

【问题讨论】:

    标签: .net unit-testing httpclient moq httpclientfactory


    【解决方案1】:

    您快到了,对于 CreateClient,您正在返回 Moq 的对象,而不是 Mocked 对象。只需要返回模拟对象如下。

     var discoDoc = // Mocked disco doc code;
    
     var client = new Mock<HttpClient>(mockHttpMessageHandler.Object);
     client.Setup(x => x.GetDiscoveryDocumentAsync(It.IsAny<string>())).Returns(discoDoc);
    
     mockHttpClientFactory.Setup(x => x.CreateClient(It.IsAny<string>())).Returns(client.Object);
    
     _httpClientService = new HttpClientService(mockHttpClientFactory.Object, mockConfig.Object);
    

    如果这是您所期望的,请告诉我。

    【讨论】:

    • 谢谢!我无法验证这是否按预期工作,因为 GetDiscoveryDocumentsAsync 由于它是一种扩展方法而导致某种问题,因此不能在设置/验证表达式中使用。但我猜如果我要解决这个问题,你的回答会解决我的问题。
    【解决方案2】:

    实际上没有办法模拟这个。对此进行测试的唯一方法是以与 IdentityModel 团队类似的方式进行 - 使用他们自己的 HttpMessageHandler 实现。 (NetworkHandler) 然后,您可以执行以下操作:

    HttpResponseMessage GetDiscoveryResponse()
    {
        var wellKnown = File.ReadAllText("openid-configuration.json");
        var response = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent(wellKnown)
        };
        return response;
    };
    var httpClient = new HttpClient(new NetworkHandler(request => {
        if (request.RequestUri.AbsoluteUri.Contains("openid-configuration"))
        {
            return GetDiscoveryResponse();
        }
        throw new NotImplementedException();
    }));
    

    【讨论】:

    • 不是必须设置HttpClient的基地址,它必须与openid-configuration.json文件的“issuer”值匹配。
    猜你喜欢
    • 2020-11-19
    • 2019-11-27
    • 1970-01-01
    • 1970-01-01
    • 2021-01-23
    • 2020-03-24
    • 1970-01-01
    • 2017-06-14
    • 2018-11-24
    相关资源
    最近更新 更多