前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ASP.NET Core 单元测试:如何 Mock HttpClient.GetStringAsync()

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

作者头像
Edi Wang
发布2021-05-27 14:38:01
1.3K0
发布2021-05-27 14:38:01
举报
文章被收录于专栏:汪宇杰博客

导语

在 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<string>())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>Programmer's 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("Programmer's Gift", result.Title);

Assert.AreEqual(targetUrl, result.TargetUrl);

Assert.AreEqual(sourceUrl, result.SourceUrl);

}

}

}

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 汪宇杰博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
日志服务
日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档