我正在尝试使用 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:'一些 参数规范(例如 Arg.Is、Arg.Any)在之后留下 最后一次通话。
这通常是由于在调用成员时使用参数规范引起的 NSubstitute 不处理(例如非虚拟成员或对 不是替代品的实例),或出于除 指定调用(例如使用 arg 规范作为返回值)。为了 示例:
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);
要解决此问题,请确保仅在调用中使用参数规范 到替代品。如果您的替代者是一个班级,请确保该成员是 虚拟。
另一个可能的原因是参数规范类型不匹配 实际参数类型,但代码由于隐式转换而编译。 例如,使用了 Arg.Any(),但 Arg.Any() 被 必填。
注意:此异常的原因可能是在之前执行的 测试。使用下面的诊断来查看任何冗余参数的类型 规格,然后找出它们是在哪里创建的。
诊断信息:
剩余(非绑定)参数规范: 任何 Uri
所有参数规范: 任何 Uri
他们是否建议我需要更改
getAsync
方法? GetAsync
没有虚拟方法
编辑:
我也尝试按如下方式删除 HttpClient 的 NSubstitute,但仍然遇到相同的错误:
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
}
首先我们需要创建
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<HttpRequestMessage>(), Arg.Any<CancellationToken>())
.Returns(mockResponse);
var result = await httpClient.GetAsync<string>("https://tempuri.org");
由于 .NET 6 向
protected virtual Send()
类引入了 HttpMessageHandler
方法(如果您使用同步 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();
}
}