28

我在一个项目中使用新的 Web API 位,我发现我不能使用 normal HttpMessageRequest,因为我需要将客户端证书添加到请求中。结果,我正在使用HttpClient(所以我可以使用WebRequestHandler)。这一切都很好,除了它不是存根/模拟友好,至少对于犀牛模拟。

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

4

3 回答 3

63

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

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

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));
        }
    }

我的简单示例只是将 in 保存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...
    }

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

于 2013-06-30T12:08:37.597 回答
39

几个月前我发布了一个名为MockHttp的库,它可能很有用。它使用HttpMessageHandler具有流畅(和可扩展)API 的自定义。您可以将模拟处理程序(或 HttpClient)注入您的服务类,它会按照配置进行响应。

下面显示基本用法。Whenand方法有Respond很多重载,包括运行自定义逻辑。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'}
于 2014-09-22T23:53:00.937 回答
11

我使用最小起订量,我可以删除 HttpClient。我认为 Rhino Mock 也是如此(我自己没有尝试过)。如果您只想存根 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 Moles Framework http://research.microsoft.com/en-us/projects/moles/ Microsoft Fakes:(如果您使用的是 VS2012 Ultimate) http://msdn.microsoft.com/en-us/library/hh549175.aspx

于 2012-09-03T12:18:30.113 回答