我试图使用NSubstitute来模拟HttpClient。下面是代码:
public static HttpClient GetHttpClient(bool isSucess = true, string methodType = "GET")
{
var mockIHttpMessageHandler = Substitute.For<IMockHttpMessageHandler>();
var mockHttpMessageHandler = Substitute.For<MockHttpMessageHandler>(mockIHttpMessageHandler);
var httpResponse = Substitute.For<HttpResponseMessage>();
httpResponse.Content = new StringContent(""test"");
if (isSucess)
httpResponse.StatusCode = HttpStatusCode.OK;
else
httpResponse.StatusCode = HttpStatusCode.NotFound;
var mockHttpClient = Substitute.For<HttpClient>(mockHttpMessageHandler);
mockHttpClient.BaseAddress = new Uri("http://localhost");
if(methodType != "POST"){
mockHttpClient.GetAsync(Arg.Any<Uri>()).ReturnsForAnyArgs(httpResponse);
}
return mockHttpClient;
}
但是,在这一行我得到了一个错误:
mockHttpClient.GetAsync(Arg.Any<Uri>()).ReturnsForAnyArgs(httpResponse);
错误是
NSubstitute.Exceptions。RedundantArgumentMatcherException:"一些参数说明(例如:参数;之后还有剩下的吗最后一次通话
这通常是由于在调用成员时使用实参规范引起的NSubstitute不处理非虚成员或调用(非替代品的实例),或用于其他目的指定调用(例如使用参数规范作为返回值)。为例子:
var sub = Substitute.For<SomeClass>(); var realType = new MyRealType(sub); // INCORRECT, arg spec used on realType, not a substitute: realType.SomeMethod(Arg.Any<int>()).Returns(2); // INCORRECT, arg spec used as a return value, not to specify a call: sub.VirtualMethod(2).Returns(Arg.Any<int>()); // INCORRECT, arg spec used with a non-virtual method: sub.NonVirtualMethod(Arg.Any<int>()).Returns(2); // CORRECT, arg spec used to specify virtual call on a substitute: sub.VirtualMethod(Arg.Any<int>()).Returns(2);
要解决这个问题,请确保只在调用中使用参数规范替代品。如果你的替代者是一个类,确保这个成员是虚拟.
另一个可能的原因是实参spec类型不匹配实际的参数类型,但代码由于隐式强制转换而编译。例如,使用了Arg.Any(),但使用了Arg.Any()必需的。
注意:此异常的原因可能在先前执行的语句中测试。使用下面的诊断来查看任何冗余参数的类型规范,然后找出它们是在哪里创建的。
诊断信息:
剩余(非绑定)参数规范:任何Uri
所有参数规范:任何Uri
他们是否建议我需要改变getAsync
方法?GetAsync
没有虚方法编辑:
我也试图删除NSubstitute为HttpClient如下,但我仍然得到相同的错误:
public static HttpClient GetHttpClient(bool isSucess = true, string methodType = "GET")
{
var mockIHttpMessageHandler = Substitute.For<IMockHttpMessageHandler>();
var mockHttpMessageHandler = Substitute.For<MockHttpMessageHandler>(mockIHttpMessageHandler);
var httpResponse = Substitute.For<HttpResponseMessage>();
httpResponse.Content = new StringContent(""test"");
if (isSucess)
httpResponse.StatusCode = HttpStatusCode.OK;
else
httpResponse.StatusCode = HttpStatusCode.NotFound;
var httpClient = new HttpClient(mockHttpMessageHandler);
httpClient = new Uri("http://localhost");
if(methodType != "POST"){
httpClient .GetAsync(Arg.Any<Uri>()).ReturnsForAnyArgs(httpResponse);
}
return httpClient
}
我知道这是一个古老的问题,但它是在谷歌搜索结果的顶部使用' c# mock httpclient使用nsubstitute ';今天,所以我想一个答案会很有用。
首先,我们需要创建HttpMessageHandler
的模拟实现。正如你所看到的,我们重写了受保护的SendAsync()
方法,并通过我们的公共Send()
方法暴露了它的主体。
public class MockHttpMessageHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Send(request, cancellationToken);
}
public virtual Task<HttpResponseMessage> Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
接下来,我们需要设置模拟。请注意,我使用的是Substitute.ForPartsOf<T>
而不是Substitute.For<T>
。
var mockHttpMessageHandler = Substitute.ForPartsOf<MockHttpMessageHandler>();
var httpClient = new HttpClient(mockHttpMessageHandler);
最后,我们现在可以使用NSubstitute在处理程序上拦截对Send()
的调用,该处理程序由HttpClient
对每个请求调用,并通过客户端返回模拟的HttpResponseMessage
。
var mockResponse = new HttpResponseMessage(HttpStatusCode.OK);
mockHttpMessageHandler.Send(Arg.Any<AnyHttpRequestMessage>(), Arg.Any<CancellationToken>())
.Returns(mockResponse);
var result = await httpClient.GetAsync<string>("https://tempuri.org");
编辑。net 6
由于。net 6在HttpMessageHandler
类中引入了protected virtual Send()
方法(如果使用同步HttpClient
调用,也需要重写),因此需要对MockHttpMessageHandler
进行一些修改:
public class MockHttpMessageHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(MockSend(request, cancellationToken));
}
protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken)
{
return MockSend(request, cancellationToken);
}
public virtual HttpResponseMessage MockSend(HttpRequestMessage request, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}