如何使用NSubstitute模拟HttpClient



我试图使用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();
}
}

最新更新