Stubbing或Mocking ASP.NET Web API HttpClient

Eri*_*k T 28 unit-testing rhino-mocks wcf-web-api

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

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

Tim*_*ong 62

作为@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));
        }
    }
Run Code Online (Sandbox Code Playgroud)

我的简单示例只是保存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...
    }
Run Code Online (Sandbox Code Playgroud)

这种方法存在一些限制,例如在一个产生多个请求或需要创建多个HttpClients的方法中,那么假处理程序可能会变得太复杂.但是,对于简单的情况,可能值得考虑.

  • 它是一个公共构造函数参数.我不认为它会改变.我非常喜欢这个答案. (6认同)

Ric*_*lay 38

几个月前我发布了一个名为MockHttp的库,它可能很有用.它使用HttpMessageHandler具有流畅(和可扩展)API 的自定义.您可以将模拟的处理程序(或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'}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很棒的图书馆人.我非常喜欢它并且已经在我们的项目中使用了. (3认同)

Spo*_*ock 11

我使用Moq,我可以删除HttpClient.我认为这对于Rhino Mock来说是一样的(我没有亲自尝试过).如果你只想存根HttpClient,下面的代码应该可以工作:

var stubHttpClient = new Mock<HttpClient>();
ValuesController controller = new ValuesController(stubHttpClient.Object);
Run Code Online (Sandbox Code Playgroud)

如果我错了,请纠正我.我想你在这里指的是在HttpClient中删除成员.

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

stubHttpClient.Setup(x => x.BaseAddress).Returns(new Uri("some_uri");
Run Code Online (Sandbox Code Playgroud)

您还提到过,您希望避免创建包装器,因为您将包装许多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;
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

我认为其他选项可能会对您有所帮助(有很多例子,所以我不会编写代码)Microsoft Moles Framework http://research.microsoft.com/en-us/projects/moles/ Microsoft Fakes :(如果您使用的是VS2012 Ultimate) http://msdn.microsoft.com/en-us/library/hh549175.aspx