存根或模拟 ASP.NET Web API HttpClient

本文关键字:Web API HttpClient NET ASP 模拟 存根 | 更新日期: 2023-09-27 18:34:57

我在项目中使用新的Web API位,并且发现我无法使用正常的HttpMessageRequest,因为我需要将客户端证书添加到请求中。结果,我正在使用HttpClient(所以我可以使用WebRequestHandler(。这一切都运行良好,除了它对存根/模拟不友好,至少对于 Rhino Mocks 来说是这样。

我通常会围绕HttpClient创建一个包装器服务,但如果可能的话,我想避免这种情况,因为我需要包装很多方法。我希望我错过了一些东西——关于如何存根HttpClient的任何建议?

存根或模拟 ASP.NET Web API HttpClient

作为@Raj已经提出的优秀想法的替代方案,也许可以降低一步,而是模拟/伪造HttpMessageHandler

如果您创建任何需要HttpClient的类接受它作为构造函数中的依赖项注入参数,那么在单元测试时,您可以传入已注入您自己的HttpMessageHandlerHttpClient。这个简单的类只有一个需要实现的抽象方法,如下所示:

public class FakeHttpMessageHandler : HttpMessageHandler
    {
    public HttpRequestMessage RequestMessage { get; private set; }
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
        RequestMessage = request;
        return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
        }
    }

我的简单示例只是将HttpRequestMessage保存在公共属性中以供以后检查并返回 HTTP 200(OK(,但您可以通过添加一个设置要返回的结果的构造函数来增强这一点。

你可以像这样使用这个类:

public void foo()
    {
    //Arrange
    var fakeHandler = new FakeHttpMessageHandler();
    var client = new HttpClient(fakeHandler);
    var SUT = new ClassUnderTest(client);
    //Act
    SUT.DomSomething();
    //Assert
    fakeHandler.RequestMessage.Method.ShouldEqual(HttpMethod.Get); // etc...
    }

这种方法存在局限性,例如,在发出多个请求或需要创建多个HttpClient的方法中,假处理程序可能会开始变得过于复杂。但是,对于简单的情况,可能值得考虑。

几个月

前我发布了一个名为MockHttp的库,它可能很有用。它使用具有流畅(且可扩展(API 的自定义HttpMessageHandler。您可以将模拟处理程序(或 HttpClient(注入到服务类中,它将在配置时响应。

下面显示了基本用法。WhenRespond 方法具有一堆重载,包括运行自定义逻辑。GitHub 页面上的文档详细介绍了更多内容。

var mockHttp = new MockHttpMessageHandler();
// Setup a respond for the user api (including a wildcard in the URL)
mockHttp.When("http://localhost/api/user/*")
        .Respond("application/json", "{'name' : 'Test McGee'}"); // Respond with JSON
// Inject the handler or client into your application code
var client = new HttpClient(mockHttp);
var response = async client.GetAsync("http://localhost/api/user/1234");
// or without async: var response = client.GetAsync(...).Result;
var json = await response.Content.ReadAsStringAsync();
// No network connection required
Console.Write(json); // {'name' : 'Test McGee'}

我使用最小起订量,我可以存根HttpClient。我认为犀牛嘲笑也是如此(我自己还没有尝试过(。如果你只想存根HttpClient,下面的代码应该可以工作:

var stubHttpClient = new Mock<HttpClient>();
ValuesController controller = new ValuesController(stubHttpClient.Object);

如果我错了,请纠正我。我猜你指的是在 HttpClient 中剔除成员。

最流行的隔离/模拟对象框架不允许你在非虚拟成员上存根/设置例如,下面的代码引发异常

stubHttpClient.Setup(x => x.BaseAddress).Returns(new Uri("some_uri");

您还提到您希望避免创建包装器,因为您将包装大量 HttpClient 成员。不清楚为什么需要包装大量方法,但您可以轻松地仅包装所需的方法。

例如:

public interface IHttpClientWrapper  {   Uri BaseAddress { get;  }     }
public class HttpClientWrapper : IHttpClientWrapper
{
   readonly HttpClient client;
   public HttpClientWrapper()   {
       client = new HttpClient();
   }
   public Uri BaseAddress   {
       get
       {
           return client.BaseAddress;
       }
   }
}

我认为可能对您有益的其他选项(有很多示例,所以我不会编写代码(Microsoft痣框架http://research.microsoft.com/en-us/projects/moles/Microsoft 假货:(如果您使用的是VS2012旗舰版(http://msdn.microsoft.com/en-us/library/hh549175.aspx