The DelegatingHandler in the MVC 4 beta Web API is not particularly easy to test due to the inheritance dependency on DelegatingHandler and the use of overridden protected methods. All is not lost though, by using the following pattern you can achieve testability of the logic with subtle changes to your existing DelegatingHandler and without breaking the Russian Doll pattern.
Create a delegate as follows:
public delegate Task<HttpResponseMessage> BaseSendAsyncDelegate(HttpRequestMessage request, CancellationToken cancellationToken);
Changing your DelegatingHandler to the following (note you’ll also need to add InternalsVisibleTo attribute for your unit test to see the handler):
public class MyTestableDelegatingHandler : DelegatingHandler
{
internal Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken, BaseSendAsyncDelegate baseSendAsync)
{
// Your before logic
// …
return baseSendAsync(request, cancellationToken).ContinueWith(
task =>
{
HttpResponseMessage response = task.Result;
// Your after logic
return response;
});
}
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// Don’t put anything else in this method
return this.SendAsync(request, cancellationToken, (r, c) => base.SendAsync(r, c));
}
}
Tests
Now the logic can be called without the overhead of the DelegatingHandler as follows:
[TestMethod]
public void MyTest()
{
MyTestableDelegatingHandler logic = new MyTestableDelegatingHandler();
HttpRequestMessage requestMessage = new HttpRequestMessage();
Task<HttpResponseMessage> result = logic.SendAsync(
requestMessage,
new CancellationToken(false),
(rm, ct) =>
{
// Do some Asserts in here for any “Before” logic
// This simulates what the inner handler (or ultimately the controller) would return
var task = new Task<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.OK));
task.Start();
return task;
});
HttpResponseMessage resultMessage = result.Result;
var status = resultMessage.StatusCode;
// Do Asserts here for any “After” logic
}