This is probably one of those RTFM type questions, however, I cannot for the life of me figure out how chain tasks together in a way that works for Asp.net WebApi.
Specifically, I am looking how to use a DelegatingHandler
to modify the response after it has been handled by the controller (add an additional header), and I am trying to unit test the DelegatingHandler
by using a HttpMessageInvoker
instance.
First Pattern
[TestMethod]
public void FirstTest()
{
var task = new Task<string>(() => "Foo");
task.ContinueWith(t => "Bar");
task.Start();
Assert.AreEqual("Bar", task.Result);
}
This fails on the assert because task.Result
returns "Foo"
Second Pattern
[TestMethod]
public void SecondTest()
{
var task = new Task<string>(() => "Foo");
var continueTask = task.ContinueWith(t => "Bar");
continueTask.Start();
Assert.AreEqual("Bar", continueTask.Result);
}
This fails on continueTask.Start()
with the exception of System.InvalidOperationException: Start may not be called on a continuation task.
Third Pattern
[TestMethod]
public void ThirdTest()
{
var task = new Task<string>(() => "Foo");
var continueTask = task.ContinueWith(t => "Bar");
task.Start();
Assert.AreEqual("Bar", continueTask.Result);
}
This test works the way I expect, however, I am not sure how to get this pattern to work with WebAPI.
Current Implementation
public class BasicAuthenticationHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var task = base.SendAsync(request, cancellationToken);
task.ContinueWith(AddWwwAuthenticateHeaderTask());
return task;
}
private static Func<Task<HttpResponseMessage>, HttpResponseMessage>
AddWwwAuthenticateHeaderTask()
{
return task =>
{
var response = task.Result;
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(
new AuthenticationHeaderValue("Basic", "realm=\"api\""));
}
return response;
};
}
}
However, when I invoke BasicAuthenticationHandler
from a unit test, my header is not added before the Assert
happens (if I debug, I notice that the header is added after the unit test fails).
[TestMethod]
public void should_give_WWWAuthenticate_header_if_authentication_is_missing()
{
using (var sut = new BasicAuthenticationHandler())
{
sut.InnerHandler = new DelegatingHttpMessageHandler(
() => new HttpResponseMessage(HttpStatusCode.Unauthorized));
using (var invoker = new HttpMessageInvoker(sut))
{
var task = invoker.SendAsync(_requestMessage, CancellationToken.None);
task.Start();
Assert.IsTrue(
task.Result.Headers.WwwAuthenticate.Contains(
new AuthenticationHeaderValue("Basic", "realm=\"api\"")));
}
}
}
If I change my production code to return the continuation task instead of the result from base.SendAsync
then I get the 2nd unit test exception about calling Start
on a continuation task.
I think I want to accomplish the third unit test pattern in my production code, however, I have no idea on how to write that.
How do I do what I want (add the header before the assert gets called)?