28

I am using the new Web API bits in a project, and I have found that I cannot use the normal HttpMessageRequest, as I need to add client certificates to the request. As a result, I am using the HttpClient (so I can use WebRequestHandler). This all works well, except that it isn't stub/mock friendly, at least for Rhino Mocks.

I would normally create a wrapper service around HttpClient that I would use instead, but I would like to avoid this if possible, as there are a lot of methods that I would need to wrap. I am hoping that I have missing something—any suggestions on how to stub HttpClient?

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Erick T
  • 7,009
  • 9
  • 50
  • 85

3 Answers3

63

As an alternative to the excellent ideas already presented by @Raj, it may be possible to go a step lower and to mock/fake the HttpMessageHandler instead.

If you make any class that needs an HttpClient accept it as a dependency injection parameter in the constructor, then when unit testing you can pass in an HttpClient that has been injected with your own HttpMessageHandler. This simple class has only one abstract method that you need to implement, as follows:

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

My trivial example just saves the HttpRequestMessage in a public property for later inspection and returns HTTP 200 (OK), but you could augment this by adding a constructor that sets the result you want returned.

You'd use this class like this:

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...
    }

There are limitations to this approach, for example in a method that makes multiple requests or needs to create multiple HttpClients, then the fake handler might start to become too complicated. However, it may be worth consideration for simple cases.

Brian
  • 3,850
  • 3
  • 21
  • 37
Tim Long
  • 13,508
  • 19
  • 79
  • 147
  • 1
    While this is a neat idea, this approach will create very brittle test. HttpClient implementation may change in the future. And you make the test dependent on the internal workings of the class. The wrapper IMHO is still the best approach. – Sunny Milenov Sep 24 '13 at 18:50
  • 6
    It's a public constructor parameter. I don't think it's going to change. I like this answer a lot. – Kugel Oct 17 '13 at 03:36
  • 1
    Fantastic, should be accepted answer. I agree with @Kugel - it's not likely to change and it's a risk I'm willing to take. – Todd Menier Mar 25 '14 at 22:19
  • @SunnyMilenov MessageHandlers are a fundamental part of HttpClient's architecture, not an implementation detail, so they aren't going to change. Besides, a public constructor is as likely to change as any other public method in the .NET Framework which is not very likely. – Panagiotis Kanavos Jun 05 '14 at 11:37
  • 1
    @PanagiotisKanavos: The proposed solution is not a "unit" test, as it does not test only the class under test, but also depends on the implementation of HttpClient. Changed or not in the future, this is not "unit test". – Sunny Milenov Jun 05 '14 at 14:50
  • Message handlers aren't "implementation". They are the components what actually execute the call and return the results. Programmers are allowed (even expected) to create their own to extend HttpClient's behavior. Enabling/disabling compression for example requires passing a configured handler instead of using the default. If you want to mock an HTTP call when using HttpClient, this is the easiest place to insert a mock answer. – Panagiotis Kanavos Jun 05 '14 at 14:53
  • @Sunny: Yes, but rewriting the way your application does requests to accommodate unit testing does not, in my opinion, seem like the best way. What if, for example, you have to provide a HTTPClient property to some external code that you can't just re-architect to use your own implementation around HTTPClient? – Craig Brett Apr 15 '15 at 14:18
  • @CraigBrett: fair enough. My statement comes from TDD stand point, where you write tests first. So, there is not "rewrite" of the application to start with :). Also, we are talking "unit" tests. If someones definition of what unit means is different, then they should write the code the way they see fit. – Sunny Milenov Apr 15 '15 at 15:58
  • @SunnyMilenov The proposed solution isn't a test by any definition - it is a fake implementation of a dependency which serves to isolate the actual unit under test from the network. – Tim Long May 07 '15 at 00:30
  • 1
    @Kai Eichinger My "weird indentation" might look odd to you but I have indented my code that way since 1981, when I studied compiler writing techniques at university and learned that the braces are in fact part of the block that they enclose, not the outer block. You can verify with LINQPad 5 and view the syntax tree Indented braces (some call "Whitesmith's style") is my organization's best practice. So please don't change my indentation, unless it is syntactically wrong, It's arrogant to assume that your PREFERENCE of indentation should override the original poster's. – Tim Long Dec 09 '15 at 19:05
39

I released a library a few months ago called MockHttp which might be useful. It uses a custom HttpMessageHandler with a fluent (and extensible) API. You can inject the mocked handler (or HttpClient) into your service class and it will respond as it was configured.

Below shows basic usage. The When and Respond methods have a bunch of overloads, including running custom logic. The documentation on the GitHub page goes into a lot more detail.

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'}
Richard Szalay
  • 83,269
  • 19
  • 178
  • 237
  • Really like this library, and the similarities to $httpBackend. I've gone with this approach and it's working well. – James Thurley Sep 23 '14 at 18:28
  • Thanks James, glad you like it! – Richard Szalay Sep 23 '14 at 22:01
  • We were obviously thinking along the same lines there. My use case was very simple and wouldn't have justified going this far, but I would imagine if you are doing a lot of work in this area then such a library would be a real time saver. I'm going to bookmark the project for next time I need something like this. – Tim Long May 07 '15 at 00:39
  • 3
    This is a great library man. I really like it and already using in our project. – Teoman shipahi Jun 09 '15 at 17:08
11

I use Moq and I can stub out the HttpClient. I think this the same for Rhino Mock (I haven’t tried by myself). If you just want to stub the HttpClient the below code should work:

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

Please correct me if I’m wrong. I guess you are referring to here is that stubbing out members within HttpClient.

Most popular isolation/mock object frameworks won’t allow you to stub/setup on non- virtual members For example the below code throws an exception

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

You also mentioned that you would like to avoid creating a wrapper because you would wrap lot of HttpClient members. Not clear why you need to wrap lots of methods but you can easily wrap only the methods you need.

For example :

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

The other options that I think might benefit for you (plenty of examples out there so I won’t write the code) Microsoft Moles Framework http://research.microsoft.com/en-us/projects/moles/ Microsoft Fakes: (if you are using VS2012 Ultimate) http://msdn.microsoft.com/en-us/library/hh549175.aspx

Kai Eichinger
  • 2,137
  • 1
  • 15
  • 13
Spock
  • 7,009
  • 1
  • 41
  • 60
  • 1
    Thanks for the answer. I ended up going with the wrapper approach. If I had to do it again, I would probably use something like TypeMock, or one of the MS solutions. – Erick T Oct 07 '12 at 04:23
  • I initially wrote my concrete implementation for a specific thing within a sealed class and tightly coupled inside.. but I realise now to get tests working nicely with `Moq` as you mentioned I must decouple some parts of the class to get a better test coverage. I knew I had to do this I just had to search for it, and then convince my self not to be lazy :D – Piotr Kula Feb 15 '17 at 12:40