Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing a custom Web API MediaTypeFormatter

I have extended the JsonMediaTypeFormatter in order to generate "root" objects in the JSON for types that are decorated with a custom attribute.

How would I unit test this formatter? I am especially interested in how to check the WriteToStreamAsync(..) method.

like image 905
bandreas Avatar asked Aug 16 '13 08:08

bandreas


1 Answers

The free O'Reilly ebook Designing Evolvable Web APIs with ASP.NET has some very useful, specific advice on how to test a MediaTypeFormatter.

Here is their test method for WriteToStreamAsync. (This is the approach I take to test WebApiContrib.Formatters.Xlsx and it works well.)

var ms = new MemoryStream();

var content = new FakeContent();
content.Headers.ContentType = new MediaTypeHeaderValue("application/atom+xml");

var formatter = new SyndicationMediaTypeFormatter();

var task = formatter.WriteToStreamAsync(typeof(List<ItemToSerialize>),
  new List<ItemToSerialize> { new ItemToSerialize { ItemName = "Test" }},
  ms,
  content,
  new FakeTransport()
);

task.Wait();

ms.Seek(0, SeekOrigin.Begin);

var atomFormatter = new Atom10FeedFormatter();
atomFormatter.ReadFrom(XmlReader.Create(ms));

Assert.Equal(1, atomFormatter.Feed.Items.Count());

Things to note:

  • FakeContent and FakeTransport are fakes of the HttpContent and TransportContext classes respectively, code for which you can find in the article.
  • Task.Wait is used to block execution until the task returned by WriteToStreamAsync finishes.
  • The output of the formatter is written to a MemoryStream, which can then be read and parsed by a suitable formatter/deserialiser so you can make test assertions.

Alternatively, you could write a sample controller implementation, start it running and test using a client to call the controller methods. This is what Chris Missal does in WebApiContrib.Formatting.Bson.

The controller doesn't need to be complicated:

public class TestController : ApiController
{
    public Item Get(int id)
    {
        return new Item { ID = id };
    }
    // ...
}

Set up the server and client:

[TestFixtureSetUp]
public void fixture_init()
{
    var config = new HttpConfiguration();
    config.Formatters.Add(new TestMediaTypeFormatter());
    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "{controller}/{id}",
        defaults: new {id = RouteParameter.Optional}
    );

    var server = new HttpServer(config);

    _client = new HttpClient(server);
    _client.BaseAddress = new Uri("http://www.test.com/");
    _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/bson"));
}

Now, in your tests, call the methods on the client and do what you want with the result:

var response = _client.GetAsync("test/1").Result;
var result = response.Content.ReadAsAsync<Item>(new HashSet<MediaTypeFormatter> {new TestMediaTypeFormatter()}).Result;
like image 87
Jordan Gray Avatar answered Sep 28 '22 02:09

Jordan Gray