Category Archives: Web API

Web API Unit Tests: Get HttpResponseMessage from IHttpActionResult

This is a quick post to share a pattern for unit testing Web API methods where async Task<IHttpActionResult> is being used as the return type.

Download the example project

During a recent code review of some unit tests for a Web API project, a stylistic issue was picked up that was pretty minor but appeared through most of our controller tests. Here’s the kind of thing we were seeing:

// Unit test excerpt...
var result = await controller.GetSomethingAsync(123);
Assert.IsNotNull(result); // Check the result isn’t null
var someResult = result as OkNegotiatedContentResult<SomeModel>; // Cast as Ok<SomeModel>
Assert.IsNotNull(someResult); // Check the cast worked

There’s nothing technically wrong with this since it verifies that we’re getting back an expected result when we call GetSomethingAsync with a value of 123. What is doesn’t do so well is express the semantics of what’s being tested. Here’s the above code as a list of pseudo-code steps:

  • Get the result of the method when called with “123”
  • Check it returned something
  • Try to cast the result to the type we’d expect if everything went okay
  • Check the cast worked

It’s those last two steps that we didn’t like. As test-driven developers, we prefer small, discrete steps in tests that help us to see precisely what’s failed when things break; as Web API developers, we prefer to view our APIs in terms of HTTP rather than abstractions. What we wanted to test might better be written as:

  • Get the response from calling the method with “123”
  • Check we have a response
  • Check that it’s an HTTP 200/OK response
  • Check that the content is as we expect

While we could have changed the return types in all our API methods from IHttpActionResult to HttpResponseMessage to achieve this, we really just wanted a way to treat the existing return types as HTTP responses. This turns out not to be trivial, though, as the HTTP response is built in a pipeline as part of an HTTP request, along with its headers, status, etc. A little digging around in the ASP.NET MVC unit tests yielded some examples of how to do just enough mocking around the ApiController to build the HttpResponseMessages in a unit test environment. Furthermore, by presenting it all as a single extension method, any additional complexity in the unit tests is kept to a minimum.

Here’s what the previous code excerpt looks like using the new approach:

HttpResponseMessage response =
    await controller.WithMockedHttpRequest(c => c.GetSomethingAsync(123));
Assert.IsNotNull(response); // Check we have a response
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); // Check the HTTP status
SomeModel someModel;
Assert.IsTrue(response.TryGetContentValue(out someModel)); // Check the content

The new method, WithMockedHttpRequest, is an extension method defined as follows:

public static class TestExtensions
{
  /// <summary>
  /// Runs the controller action within a mocked HTTP context
  /// </summary>
  /// <typeparam name="T">The controller type</typeparam>
  /// <typeparam name="TReturn">The controller action return type</typeparam>
  /// <param name="controller"></param>
  /// <param name="func">The controller code to execute within a mocked HTTP context</param>
  /// <returns>The HttpResponseMessage containing the action result</returns>
  public static async Task<HttpResponseMessage> WithMockedHttpRequest<T, TReturn>(
    this T controller, Func<T, Task<TReturn>> func) where T : ApiController
  {
    // Build a mocked JSON request/response configuration
    MediaTypeFormatter expectedFormatter = new StubMediaTypeFormatter();
    MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue("text/json");
    ContentNegotiationResult negotiationResult = new ContentNegotiationResult(expectedFormatter, expectedMediaType);
    Mock<IContentNegotiator> contentNegotiator = new Mock<IContentNegotiator>();
contentNegotiator
      .Setup(n => n.Negotiate(It.IsAny<Type>(), It.IsAny<HttpRequestMessage>(), It.IsAny<IEnumerable<MediaTypeFormatter>>()))
      .Returns(negotiationResult);
   using (HttpConfiguration configuration = CreateConfiguration(new StubMediaTypeFormatter(), contentNegotiator.Object))
    {
      controller.Configuration = configuration;
      // Build a mocked request context from which to build the response
      using (HttpRequestMessage request = new HttpRequestMessage())
      {
        controller.Request = request;
        var actionResult = await func.Invoke(controller);
        // Create the response from the action result
        if (typeof (IHttpActionResult).IsAssignableFrom(typeof (TReturn)))
        {
          return await ((IHttpActionResult) actionResult).ExecuteAsync(CancellationToken.None);
        }
        else
        {
          return await Task.FromResult(request.CreateResponse(actionResult));
        }
      }
    }
  }

  private class StubMediaTypeFormatter : MediaTypeFormatter
  {
    public override bool CanReadType(Type type)
    {
      return true;
    }
    public override bool CanWriteType(Type type)
    {
      return true;
    }
  }

  private static HttpConfiguration CreateConfiguration(MediaTypeFormatter formatter, IContentNegotiator contentNegotiator)
  {
    HttpConfiguration configuration = new HttpConfiguration();
    configuration.Formatters.Clear();
    configuration.Formatters.Add(formatter);
    configuration.Services.Replace(typeof(IContentNegotiator), contentNegotiator);
    return configuration;
  }
}

The example is based in part on the ASP.NET MVC unit tests for ContentNegotiatedResult. Those tests are a good place to start if you want to get a feel for how the HTTP responses are built.

– Mike Clift

JSON Services: A Comparison of WCF and Web API

This blog post shows two implementations of a really simple JSON calculator service: one using WCF and one using Web API. To show the services in action, there’s also a single webpage that exposes the calculator services though a Web form.

Download the example project

Blog post header

WCF

I’ve used a self-hosted WCF service for this example as I want control over the URL through which it’s accessed. I start by creating a new Console Application and adding references to System.Runtime.Serialization, System.ServiceModel and System.ServiceModel.Web.

The service will expose a method to add two numbers together, so I add a class to contain the parameters to this method:

[DataContract]
public class AddParameters
{
    [DataMember(Name = "left")]
    public decimal Left { get; set; }

    [DataMember(Name = "right")]
    public decimal Right { get; set; }
}

I also create a class to contain the result of the calculation.

[DataContract]
public class CalculationResult
{
    [DataMember(Name = "result")]
    public decimal Result { get; set; }
}

Now I’m ready to define the WCF service interface…

[ServiceContract]
public interface ICalculator
{
    [OperationContract]
    CalculationResult Add(AddParameters addParameters);
}

…and its implementation.

public class Calculator : ICalculator
{
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, 
        ResponseFormat = WebMessageFormat.Json,
        BodyStyle = WebMessageBodyStyle.Wrapped)]
    public CalculationResult Add(AddParameters addParameters)
    {
        decimal result = addParameters.Left + addParameters.Right;

        return new CalculationResult
        {
            Result = result
        };
    }
}

The code to create the service host is pretty straightforward. In Program.cs, I update the Main(…) method:

static void Main(string[] args)
{
    using (ServiceHost serviceHost = new ServiceHost(typeof(Calculator)))
    {
        serviceHost.Open();

        Console.WriteLine("The Calculator service is available at:");
        foreach (var endpoint in serviceHost.Description.Endpoints)
        {
            Console.WriteLine(endpoint.Address);
        }

        Console.WriteLine();
        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }
}

Next, I add the WCF service configuration that’ll allow the service to be called from within a webpage.

<system.serviceModel>
  <services>
    <service name="WcfJsonServices.Calculator">
      <endpoint address="http://localhost:8080/Calculator"
                binding="webHttpBinding"
                contract="WcfJsonServices.ICalculator"/>
    </service>
  </services>
  <behaviors>
    <endpointBehaviors>
      <behavior>
        <webHttp />
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>

Finally, since I’m going to run this on a system with UAC enabled, I give my user account the necessary permission to create an HTTP service on port 8080. To do this, I use the “Run as Administrator” option to open a new Command Prompt window and then enter the following command:

netsh http add urlacl url=http://+:8080/ user="Mike"

WebAPI

To make the demo hang together as simply as possible, I’m going to host my client webpage and my Web API service in the same project. I start by creating a new ASP.NET Web Application project and choosing the Web API project template when prompted.

As per the WCF example, I create classes to contain my input and output parameters for my Add service. I create them both under the Models folder.

public class AddParameters
{
    public decimal Left { get; set; }

    public decimal Right { get; set; }
}

public class CalculationResult
{
    public decimal Result { get; set; }
}

Notice that the above classes don’t need to be declared as Serializable since the ASP.NET MVC framework automatically handles the JSON serialisation.

Next, I create a new controller class that’ll expose the addition service. I create the AddController class under the Controllers folder.

public class AddController : ApiController
{
    public CalculationResult Post(AddParameters addParameters)
    {
        decimal result = addParameters.Left + addParameters.Right;

        return new CalculationResult
        {
            Result = result
        };
    }
}

Note that there’s no need to declare a separate service interface. Just inherit from ApiController and ensure the method name begins with the HTTP verb (“POST’ in this case) that the client is expected to use.

I don’t need to add any special HTTP routes as the default handles it nicely—the new addition service has the URL /api/Add (see the default mappings in the WebApiConfig class under the App_Start folder) and it accepts POST requests.

The Client Web Page

To test the two services side-by-side, I want a single Web page that lets me fire JSON requests at each service and displays the results they return. As I’m going to use jQuery to do most of the client-side work, I first add the following line into the <head> section of _Layout.cshtml, which is located under /Views/Shared:

@Scripts.Render("~/bundles/jquery")

Now, I delete the contents of Index.cshtml, which is located under /Views/Home, and import my default layout.

@{
    ViewBag.Title = "Home Page";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Now I add the UI components that’ll allow users to generate the service requests.

<div class="jumbotron">
    <h1>JSON Client Samples</h1>
</div>
<div class="row">
    <div class="row">
        <h2>Use WCF</h2>
        <p>
            Add <input type="text" id="wcfLeft" />
            to <input type="text" id="wcfRight" />
            <input type="button" id="wcfAdd" value="=" />
            <span id="wcfAnswer">&nbsp;</span>
        </p>
    </div>
    <div class="row">
        <h2>Use WebAPI</h2>
        <p>
            Add <input type="text" id="waLeft" />
            to <input type="text" id="waRight" />
            <input type="button" id="waAdd" value="=" />
            <span id="waAnswer">&nbsp;</span>
        </p>
    </div>
    <div>
        <p id="error"></p>
    </div>
</div>

Now I add a script block and begin wiring up the Web UI with the JSON services. I start by wiring up the each of the two buttons so that the “click” event will cause a method to be called with the values of the adjacent text boxes. For WCF, wcfAdd(…) will be called. For Web API, waAdd(…) will be called.

$(function () {
    // Bind the WCF button
    $("#wcfAdd").bind("click", function () {
        var left = $("#wcfLeft")[0].value;
        var right = $("#wcfRight")[0].value;
        wcfAdd(left, right);
    });

    // Bind the WebAPI button
    $("#waAdd").bind("click", function () {
        var left = $("#waLeft")[0].value;
        var right = $("#waRight")[0].value;
        waAdd(left, right);
    });
});

The function that calls WCF begins with some object literal notation to set up the parameters expected by the service (just a single parameter in this case: addParameters). The parameters are then converted into a JSON string. Finally, the JSON is POSTed to the service. On success, the calculation result is displayed; on failure, the user is notified that something went wrong.

// Call the WCF service to add the numbers together
function wcfAdd(left, right) {
    // Prepare the instance of AddParameters with the two numbers
    var parameters = {
        addParameters: {
            left: left,
            right: right
        }
    };
    // Convert to an escaped JSON string
    var json = JSON.stringify(parameters);
    // Make the request
    $.ajax({
        type: "POST",
        url: "http://localhost:8080/Calculator/Add",
        data: json,
        contentType: "application/json; charset=utf-8",
        success: function (result) {
            $("#wcfAnswer").text(result.AddResult.result);
        },
        error: function (xhr, msg, ex) {
            handleError(xhr, msg, ex);
        }
    });
}

The Web API version doesn’t need to explicitly define addParameters as a named object literal. Instead, it just needs to create an object with the correct members: Left and Right. The rest is almost identical to the WCF version, the only differences being the sevice URL and the structure of the returned result.

// Call the WebAPI service to add the numbers together
function waAdd(left, right) {
    // Prepare the instance of AddParameters with the two numbers
    var parameters = {
        Left: left,
        Right: right
    };
    // Convert to an escaped JSON string
    var json = JSON.stringify(parameters);
    // Make the request
    $.ajax({
        type: "POST",
        url: "/api/Add",
        data: json,
        contentType: "application/json; charset=utf-8",
        success: function (result) {
            $("#waAnswer").text(result.Result);
        },
        error: function (xhr, msg, ex) {
            handleError(xhr, msg, ex);
        }
    });
}

Finally, I add a method to notify the user of any errors.

function handleError(xhr, msg, ex) {
    $("#error").html(xhr.responseText);
    alert("Call failed: [" + xhr.status + "] " + ex);
}

Demo

The WCF service application needs to be running for the demo to work. When the Web application is launched, a simple form allows the use to call each service.

Screenshot of the test Web page

Conclusions

I’ve shown how to create a JSON service that’s accessible from a Web page with jQuery by using two different technologies: WCF and Web API. Going through this exercise has highlighted a few things for me about each technology.

WCF: Pros and Cons

  • Pro: I like that WCF can be hosted in a number of different ways, from a stand-alone console application to an IIS application.
  • Con: Perhaps because of WCF’s flexibility in terms of the transports and message formats it supports, getting the configuration right can be difficult. What makes it worse is that it really doesn’t help you when things aren’t set up correctly: you don’t get a chance to intercept an exception; you don’t get any hints about what the service is expecting; most of the time, you just get a boilerplate HTTP exception with no clues as to the cause of what’s wrong.

Web API: Pros and Cons

  • Pro: It’s really simple to get up and running, especially if you’re already used to the ASP.NET MVC framework.
  • Pro: Web API benefits from the transparency of the ASP.NET MVC framework: when something isn’t configured correctly or isn’t being called in the right way, there’s still usually somewhere where you can set a breakpoint or intercept an exception so you can see what’s going on. Having the Request and Response properties right there can also be really useful whilst debugging.
  • Con: This is a very pernickety point, but it feels like Web API isn’t that great as an API presentation framework. WCF’s requirement that any service be declared as an interface makes it very clear where your API’s boundaries lie. Not only does Web API not require your interfaces to be declared, it also obscures things by mapping HTTP verbs to methods by their names and signatures. Of course, this convention-over-configuration approach is one of the cornerstones of the technology that brings many advantages, but it also ties the API to HTTP.

At the time of writing, I’m a newbie to Web API but in a situation where I were consuming JSON services from the browser, I’d lean toward Web API as the framework of choice. This would be especially true if the consuming Web application were already an ASP.NET MVC project—the benefits of using the same underlying technology for the Web application and JSON services would be difficult to ignore.

Of course, this post has focused on a very small, specific scenario. WCF is a much broader technology than Web API in terms of the transports, methods of communication and types of hosting it supports, whilst Web API expands upon the very successful ASP.NET MVC to make it easy to write services for browsers. There’s a great blog post by Ido Flatow, “WCF or ASP.NET Web APIs? My two cents on the subject”, that gives a little bit of the history behind these two technologies and presents a range of scenarios in which one might be a better fit than the other.

For more information about Web API, the Official ASP.NET site is a good place to start. For WCF, visit the WCF portal on MSDN.

  – Mike Clift