当前位置:首页 > 人工智能

ASP.NET Core 单元测试之如何 Mock HttpClient.GetStringAsync()

 

本文转载自微信公众号「汪宇杰博客」,单元作者汪宇杰。测试转载本文请联系汪宇杰博客公众号。单元

在 ASP.NET Core 单元测试中模拟HttpClient.GetStringAsync() 的测试技巧。

问题

下面这个代码

var html = await _httpClient.GetStringAsync(sourceUrl); 

如果按正常思路像这样去 Mock HttpClient.GetStringAsync()

var httpClientMock = new Mock<HttpClient>(); httpClientMock     .Setup(p => p.GetStringAsync(It.IsAny<string>()))     .Returns(Task.FromResult("...")); 

Moq 框架就会爆

Exception

System.NotSupportedException : Unsupported expression: p => p.GetStringAsync(It.IsAny())Non-overridable members (here: HttpClient.GetStringAsync) may not be used in setup / verification expressions. 

解决方法

我们需要 Mock HttpClient 底层使用的亿华云计算单元 HttpMessageHandler 而不是 HttpClient

var handlerMock = new Mock<HttpMessageHandler>(); var magicHttpClient = new HttpClient(handlerMock.Object); 

然后我花了 9.96 分钟研究了 HttpClient.GetStringAsync() 的源代码,发现它最终调用的测试是服务器托管 SendAsync() 方法

private async Task<string> GetStringAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) {      // ...     response = await base.SendAsync(request, cts.Token).ConfigureAwait(false);     // ... } 

源代码位置:https://source.dot.net/#System.Net.Http/System/Net/Http/HttpClient.cs,170

因此,我们的单元 Mock Setup 如下:

handlerMock     .Protected()     .Setup<Task<HttpResponseMessage>>(         "SendAsync",         ItExpr.IsAny<HttpRequestMessage>(),         ItExpr.IsAny<CancellationToken>()     )     .ReturnsAsync(new HttpResponseMessage     {          StatusCode = HttpStatusCode.OK,         Content = new StringContent("the string you want to return")     })     .Verifiable(); 

现在 Mock 就能运行成功了!

最后附上完整的 UT 代码供参考:

using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Moq; using Moq.Protected; using NUnit.Framework; namespace Moonglade.Pingback.Tests {      [TestFixture]     public class PingSourceInspectorTests     {          private MockRepository _mockRepository;         private Mock<ILogger<PingSourceInspector>> _mockLogger;         private Mock<HttpMessageHandler> _handlerMock;         private HttpClient _magicHttpClient;         [SetUp]         public void SetUp()         {              _mockRepository = new(MockBehavior.Default);             _mockLogger = _mockRepository.Create<ILogger<PingSourceInspector>>();             _handlerMock = _mockRepository.Create<HttpMessageHandler>();         }         private PingSourceInspector CreatePingSourceInspector()         {              _magicHttpClient = new(_handlerMock.Object);             return new(_mockLogger.Object, _magicHttpClient);         }         [Test]         public async Task ExamineSourceAsync_StateUnderTest_ExpectedBehavior()         {              string sourceUrl = "https://996.icu/work-996-sick-icu";             string targetUrl = "https://greenhat.today/programmers-special-gift";             _handlerMock                 .Protected()                 .Setup<Task<HttpResponseMessage>>(                     "SendAsync",                     ItExpr.IsAny<HttpRequestMessage>(),                     ItExpr.IsAny<CancellationToken>()                 )                 .ReturnsAsync(new HttpResponseMessage                 {                      StatusCode = HttpStatusCode.OK,                     Content = new StringContent($"<html>" +                                                 $"<head>" +                                                 $"<title>Programmers Gift</title>" +                                                 $"</head>" +                                                 $"<body>Work 996 and have a <a href=\"{ targetUrl}\">green hat</a>!</body>" +                                                 $"</html>")                 })                 .Verifiable();             var pingSourceInspector = CreatePingSourceInspector();             var result = await pingSourceInspector.ExamineSourceAsync(sourceUrl, targetUrl);             Assert.IsFalse(result.ContainsHtml);             Assert.IsTrue(result.SourceHasLink);             Assert.AreEqual("Programmers Gift", result.Title);             Assert.AreEqual(targetUrl, result.TargetUrl);             Assert.AreEqual(sourceUrl, result.SourceUrl);         }     } } 

分享到:

滇ICP备2023006006号-16