C# 如何在.NETCore2.1中使用Moq模拟新的HttpClientFactory
.NET Core 2.1附带了一个名为C# 如何在.NETCore2.1中使用Moq模拟新的HttpClientFactory,c#,unit-testing,moq,asp.net-core-2.1,httpclientfactory,C#,Unit Testing,Moq,Asp.net Core 2.1,Httpclientfactory,.NET Core 2.1附带了一个名为HttpClientFactory的新工厂,但我不知道如何模拟它来对一些包括REST服务调用的方法进行单元测试 正在使用.NET Core IoC容器注入工厂,该方法所做的是从工厂创建一个新客户端: var client = _httpClientFactory.CreateClient(); 然后使用客户端从REST服务获取数据: var result = await client.GetStringAsync(url); HttpClientFac
HttpClientFactory
的新工厂,但我不知道如何模拟它来对一些包括REST服务调用的方法进行单元测试
正在使用.NET Core IoC容器注入工厂,该方法所做的是从工厂创建一个新客户端:
var client = _httpClientFactory.CreateClient();
然后使用客户端从REST服务获取数据:
var result = await client.GetStringAsync(url);
HttpClientFactory的
var mockFactory = new Mock<IHttpClientFactory>();
然后,在执行测试时,可将工厂注入受测的从属系统
如果不希望客户端调用实际端点,则需要创建一个伪委托处理程序来拦截请求
用于伪造请求的处理程序存根示例
public class DelegatingHandlerStub : DelegatingHandler {
private readonly Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> _handlerFunc;
public DelegatingHandlerStub() {
_handlerFunc = (request, cancellationToken) => Task.FromResult(request.CreateResponse(HttpStatusCode.OK));
}
public DelegatingHandlerStub(Func<HttpRequestMessage, CancellationToken, Task<HttpResponseMessage>> handlerFunc) {
_handlerFunc = handlerFunc;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
return _handlerFunc(request, cancellationToken);
}
}
这个代码为我抛出了这个异常,
System.InvalidOperationException:请求没有关联的配置对象,或者提供的配置为空
因此,在测试方法中包含了这一点,它是有效的
var configuration = new HttpConfiguration();
var request = new HttpRequestMessage();
request.SetConfiguration(configuration);
除了前面介绍如何设置存根的文章外,您还可以使用Moq设置DelegatingHandler
:
var clientHandlerMock = new Mock<DelegatingHandler>();
clientHandlerMock.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK))
.Verifiable();
clientHandlerMock.As<IDisposable>().Setup(s => s.Dispose());
var httpClient = new HttpClient(clientHandlerMock.Object);
var clientFactoryMock = new Mock<IHttpClientFactory>(MockBehavior.Strict);
clientFactoryMock.Setup(cf => cf.CreateClient()).Returns(httpClient).Verifiable();
clientFactoryMock.Verify(cf => cf.CreateClient());
clientHandlerMock.Protected().Verify("SendAsync", Times.Exactly(1), ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>());
var clientHandlerMock=new Mock();
clientHandlerMock.Protected()
.Setup(“sendsync”、ItExpr.IsAny()、ItExpr.IsAny())
.ReturnsAsync(新的HttpResponseMessage(HttpStatusCode.OK))
.可验证();
clientHandlerMock.As().Setup(s=>s.Dispose());
var httpClient=新的httpClient(clientHandlerMock.Object);
var clientFactoryMock=newmock(MockBehavior.Strict);
clientFactoryMock.Setup(cf=>cf.CreateClient()).Returns(httpClient.Verifiable();
验证(cf=>cf.CreateClient());
clientHandlerMock.Protected();
我使用了@Nkosi中的示例,但是使用了.NET 5
我得到了HttpConfiguration
所需的Microsoft.AspNet.WebApi.Core
软件包的以下警告
警告NU1701程序包“Microsoft.AspNet.WebApi.Core 5.2.7”被删除
已使用“.NETFramework,版本=v4.6.1”还原,
.NETFramework,版本=v4.6.2,.NETFramework,版本=v4.7,
.NETFramework,版本=v4.7.1,.NETFramework,版本=v4.7.2,
.NETFramework,Version=v4.8'而不是项目目标框架
“net5.0”。此软件包可能与您的项目不完全兼容
不使用HttpConfiguration
完成示例:
private LoginController GetLoginController()
{
var expected = "Hello world";
var mockFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(expected)
});
var httpClient = new HttpClient(mockMessageHandler.Object);
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient);
var logger = Mock.Of<ILogger<LoginController>>();
var controller = new LoginController(logger, mockFactory.Object);
return controller;
}
private LoginController GetLoginController()
{
var expected=“Hello world”;
var mockFactory=new Mock();
var mockMessageHandler=new Mock();
mockMessageHandler.Protected()
.Setup(“sendsync”、ItExpr.IsAny()、ItExpr.IsAny())
.ReturnsAsync(新的HttpResponseMessage
{
StatusCode=HttpStatusCode.OK,
内容=新的StringContent(预期)
});
var httpClient=新的httpClient(mockMessageHandler.Object);
mockFactory.Setup(=>wk.CreateClient(It.IsAny())).Returns(httpClient);
var logger=Mock.Of();
var控制器=新的LoginController(记录器,mockFactory.Object);
返回控制器;
}
资料来源:
另一种方法可能是创建一个额外的类,该类将在内部调用服务。这个类很容易被模仿。
这不是对这个问题的直接回答,但它似乎不那么复杂,更易于测试。当我尝试用Moq做类似的事情时,我从Moq得到了一个错误,不支持扩展方法的设置,这就是为什么我在第一个实例中问这个问题的原因,明天我将试用您的版本。@MauricioAtanache注意,我设置的是实际的接口成员,而不是扩展方法。扩展最终将调用接口方法。如果有人好奇,我在查找可在HttpRequestMessageExtensions中找到的
request.CreateResponse()
的适当引用时遇到问题。我通过将Microsoft.AspNet.WebApi.Core包添加到projectWow中,将其添加到了我的单元测试项目中。多痛苦啊。。。我希望CreateClient()
方法返回一个接口,而不是具体的HttpClient类型。您应该将.Returns(client)
更改为。Returns(()=>newhttpclient(clientHandlerStub))
以防止在第二次调用时返回已处理的HttpClient
s。
var configuration = new HttpConfiguration();
var request = new HttpRequestMessage();
request.SetConfiguration(configuration);
var clientHandlerMock = new Mock<DelegatingHandler>();
clientHandlerMock.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK))
.Verifiable();
clientHandlerMock.As<IDisposable>().Setup(s => s.Dispose());
var httpClient = new HttpClient(clientHandlerMock.Object);
var clientFactoryMock = new Mock<IHttpClientFactory>(MockBehavior.Strict);
clientFactoryMock.Setup(cf => cf.CreateClient()).Returns(httpClient).Verifiable();
clientFactoryMock.Verify(cf => cf.CreateClient());
clientHandlerMock.Protected().Verify("SendAsync", Times.Exactly(1), ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>());
private LoginController GetLoginController()
{
var expected = "Hello world";
var mockFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent(expected)
});
var httpClient = new HttpClient(mockMessageHandler.Object);
mockFactory.Setup(_ => _.CreateClient(It.IsAny<string>())).Returns(httpClient);
var logger = Mock.Of<ILogger<LoginController>>();
var controller = new LoginController(logger, mockFactory.Object);
return controller;
}