Unit Testing HttpClient In An ASP.NET MVC Controller

November 7 2013

The nice thing about HttpClient is that it was designed with unit testing in mind, unlike previous .NET http objects like WebClient and WebRequest which required you to write wrappers. As such you don’t need to use dependency injection in order to unit test HttpClient, which keeps your code that much more readable.

So, how to do it?

First, you need to pass HttpClient into the controller’s constructor in your MVC project. Again, the nice thing is that you don’t have to do dependency injection for this with a framework like Unity. The easiest way to do that is via constructor chaining, like this:

public class MyController: Controller
  private readonly HttpClient _httpClient;

  public MyController() : this(new HttpClient())

  public MyController(HttpClient httpClient)
    _httpClient= httpClient;


Then, in your tests, you’ll need to fashion up a FakeHandler like this:

 public class FakeHandler : DelegatingHandler
            public HttpResponseMessage Response { get; set; }

            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                        CancellationToken cancellationToken)
                return Task.Factory.StartNew(() => Response);

which you will use when you new up your HttpClient in your tests. Now, in you’ll need to set up the HttpResponseMessage before you instantiate the HttpClient in your tests. If you are expecting string content in the response, in the case of getting JSON for instance, here’s how that might look:

string json;
using (StreamReader reader = new StreamReader("json.txt"))
        json = reader.ReadToEnd();
var response = new HttpResponseMessage(HttpStatusCode.OK);
byte[] array = Encoding.ASCII.GetBytes(json);
ByteArrayContent content = new ByteArrayContent(array);
response.Content = content;

Here we are grabbing some pre crafted JSON from the file system, throwing it in a byte array and then giving that to our HttpResponseMessage.

Now, that we have our expected response set up, we use that in our FakeHandler when we instantiate the HttpClient for the test:

var httpClient = new HttpClient(new FakeHandler
       Response = response,
       InnerHandler = new HttpClientHandler()
MyContoller myController = new MyController(httpClient);

And, there you have it!

Taking The M out of MVC - JSON, The dynamic Keyword, LINQ

May 25 2011

Okay, so I’ve been working on a new MVC project in all the data is stored as JSON. The plan is to have JSON deserialized in the controller (using the WCF JSON classes) and then pass the deserialized JSON to the view. The general thinking is that we won't have any models, just controllers and views that work with dynamically created object graphs from JSON.

So, I started implementing.  Works great. For example, here’s controller code that grabs some JSON out of blob storage, deserializes it, and returns it to the view:

public ActionResult Index(string name) { ICloudBlobContainer container = cloudBlobClient.GetContainerReference("applications"); ICloudBlob blob = container.GetBlobReference(string.Format("{0}.txt",name)); string json = blob.DownloadText(); JsonObject achievementModel = JsonValue.Parse(json) as JsonObject; return View(achievementModel); }

And here's what the view looks like:

@model dynamic <h2>@Model.Name</h2> <h3>@Model.Description</h3>

Okay, so far so good. Notice how the JSON gets to the view as dynamic and the view can party on it.

But, here's where things got tricky. What happens when I want to LINQ queries over the JSON and then pass it to my view? This is going to be a common scenario for the application, where the controller filters the model, if you will, before handing it off to the view.

For example, consider the following code:

var achievementsByCategory = from JsonValue v in achievementModel["Achievements"] group v by v["Category"].ReadAs<string>("") into grp select new { Category = grp.Key, Items = grp }; return View(achievementsByCategory);

Basically, I'm doing a group by clause on the object tree created in the deserialized JSON and creating an anonymous type, then returning that to the view. Nice idea, but MVC and anonymous types are not friends. Run this code and when you attempt to read the model and look at the properties , you'll get an exception of the like:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled by user code -- 'object' does not contain a definition for 'Category'

So, what to do? One solution would be to create a typed object as part of the LINQ result and return that to the view. But that defeats the whole point of the architecture, which is to stay flexible, have no strict typing, but still getting the goodness of LINQ. What I realized is that I need to trick LINQ into returning a dynamic type, not an anonymous type. How to do this? Well, I realized that all the Json classes implement IDynamicMetaObjectProvider.

 Awesome! I could have LINQ return an IEnumerable of JsonObjects, which I could then cast as dynamic in my view and party on. So, the code looks like this:

var achievementsByCategory = from JsonValue v in achievementModel["Achievements"] group v by v["Category"].ReadAs<string>("") into grp select new JsonObject { {"Category", grp.Key}, {"Items", new JsonArray(grp)} }; return View(achievementsByCategory);

What's clever about this code is that my LINQ query is returning a set of JsonValue objects, which I can pass directly to the ctor of JsonArray.  And my view is happy, because it is getting something it knows how to handle: an IEnumerable. I can then walk the JsonObject results in the view, casting them back to dynamic and getting all the lovely property syntax:

@foreach (dynamic group in Model) { <h3>@group.Category</h3> <ul> @foreach (dynamic item in group.Items) { <li>@item.Name - @item.Description </li> } </ul> }
Pretty slick, methinks. My JSON can now change, morph, etc. and my code is rock solid. But at the same time, I get all the goodness of LINQ. And my front end Razor code ends up feeling more like Javascript than C#. It is the weird, trippy marriage of JSON and dynamic. I'm stoked about it. In this bizarre way, it takes the model out of MVC.