Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I test a custom DelegatingHandler in the ASP.NET MVC 4 Web API?

I've seen this question come up in a few places, and not seen any great answers. As I've had to do this myself a few times, I thought I'd post my solution. If you have anything better, please post.

N.B. This is using ASP.NET MVC 4 Beta 2 version of Web API - future versions may change!

Update: This still works in ASP.NET MVC 4 RC

like image 533
James World Avatar asked Mar 20 '12 15:03

James World


People also ask

What is Delegatinghandler in Web API?

Typically, a series of message handlers are chained together. The first handler receives an HTTP request, does some processing, and gives the request to the next handler. At some point, the response is created and goes back up the chain. This pattern is called a delegating handler.

Is it possible to unit test Web API?

We have an option to create a Unit test project when we create a Web API project. When we create a unit test project with a web API project, Visual Studio IDE automatically adds Web API project reference to the unit test project.


2 Answers

In this approach, I create a TestHandler and set it as the InnerHandler property of the handler under test.

The handler under test can then be passed to an HttpClient - this may seem unintuitive if you are writing a server-side handler, but this is actually a great light-weight way to test a handler - it will be called in the same way it would in a server.

The TestHandler will just return an HTTP 200 by default, but it's constructor accepts a function you can use to make asserts about the request message passed in from the handler under test. Finally you can make asserts on the result of the SendAsync call from the client.

Once everything is set up, call SendAsync on the client instance to invoke your handler. The request will be passed into your handler, it will pass this on to the TestHandler (assuming it passes the call on) which will then return a response back to your handler.

The test handler looks like this:

public class TestHandler : DelegatingHandler {     private readonly Func<HttpRequestMessage,         CancellationToken, Task<HttpResponseMessage>> _handlerFunc;      public TestHandler()     {         _handlerFunc = (r, c) => Return200();     }      public TestHandler(Func<HttpRequestMessage,         CancellationToken, Task<HttpResponseMessage>> handlerFunc)     {         _handlerFunc = handlerFunc;     }      protected override Task<HttpResponseMessage> SendAsync(         HttpRequestMessage request, CancellationToken cancellationToken)     {         return _handlerFunc(request, cancellationToken);                   }      public static Task<HttpResponseMessage> Return200()     {         return Task.Factory.StartNew(             () => new HttpResponseMessage(HttpStatusCode.OK));     } } 

Example usage with an imagined MyHandler under test. Uses NUnit for the asserts.:

var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://test.com"); httpRequestMessage.Headers.Add("username", "test");  var handler = new MyHandler() {     InnerHandler = new TestHandler((r,c) =>     {         Assert.That(r.Headers.Contains("username"));         return TestHandler.Return200();     }) };  var client = new HttpClient(handler); var result = client.SendAsync(httpRequestMessage).Result;  Assert.That(result.StatusCode, Is.EqualTo(HttpStatusCode.OK)); 

The default behaviour of TestHandler is probably fine for many tests and makes the code simpler. The setup of the handler under test then looks like this:

var handler = new MyHandler(); handler.InnerHandler = new TestHandler(); 

I like this approach because it keeps all the assertions in the test method, and the TestHandler is very reusable.

like image 59
James World Avatar answered Oct 06 '22 08:10

James World


I was just looking for the same thing but came up with a more concise approach that didn't use http client. I wanted a test to assert the message handler consumed a mocked logging component. I didn't really need the inner handler to function, just to "stub" it out to satisfy the unit test. Works for my purpose :)

//ARRANGE         var logger = new Mock<ILogger>();         var handler= new ServiceLoggingHandler(logger.Object);         var request = ControllerContext.CreateHttpRequest(Guid.NewGuid(), "http://test",HttpMethod.Get);          handler.InnerHandler = new Mock<HttpMessageHandler>(MockBehavior.Loose).Object;          request.Content = new ObjectContent<CompanyRequest>(Company.CreateCompanyDTO(), new JsonMediaTypeFormatter());         var invoker = new HttpMessageInvoker(handler);          //ACT         var result = invoker.SendAsync(request, new System.Threading.CancellationToken()).Result;  //ASSERT <Your assertion> 
like image 43
Spencer Avatar answered Oct 06 '22 10:10

Spencer