You just need to wire FooHandler
's InnerHandler
to an instance of type HttpMessageHandler
.
You can mock using AutoFixture
as fixture.Create<HttpMessageHandler>()
, or you can handle-roll your own, or you can use the awesome MockHttpMessageHandler
.
TLDR
There are a few small things off with the answers here so I thought of adding my answer. I recall seeing similar code as mentioned in Ray's answer, and I was thrown off by the usage of Task.Factory.StartNew()
in there, which is totally unnecessary. Moq.Protected
is a bit brittle for my taste; it's ok in this situation as "SendAsync"
method won't change mostly.
The Delegating Handler follows the chain-of-responsibility pattern where one can form a chain. One doesn't get to control the last handler which will be HttpMessageHandler
, an abstract class.
So, the simplest form of chain is,
HttpClient -> HttpMessageHandler impl
In your case, the chain is,
HttpMessageInvoker -> FooHandler -> HttpMessageHandler impl
It's convenient to use the plumbing class HttpMessageInvoker
for testing, instead of the porcelain class HttpClient
. In your example, HttpMessageInvoker
ctor handles the part of wiring up its InnerHandler
with FooHandler
, but you need to wire up FooHandler
's InnerHandler
. Note that one doesn't need to do this for HttpClient
as there exists a helper method, HttpClientFactory.Create()
which handles the wiring. But there isn't an HttpMessageInvokerFactory.Create()
method. I have few tests in my repo where I have scenarios with few handlers in the chain, and I eventually ended up writing that helper method (pasted below).
Note that only HttpMessageInvokerFactory
handles the wiring part; with others, one has to do it explicitly.
Examples
Putting it all together, let's say one has a simple Delegating Handler to test as RelayHandler
which simply relays the http request,
/// <summary>
/// Simply relays request, just for fun.
/// </summary>
public class RelayHandler : DelegatingHandler
{
/// <inheritdoc cref="RelayHandler" />
public RelayHandler()
{ }
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken);
}
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/src/rm.DelegatingHandlers/RelayHandler.cs
One can test it as,
[Test]
public async Task Relays()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var relayHandler = new RelayHandler();
using var invoker = HttpMessageInvokerFactory.Create(
fixture.Create<HttpMessageHandler>(), relayHandler);
using var requestMessage = fixture.Create<HttpRequestMessage>();
using var response = await invoker.SendAsync(requestMessage, CancellationToken.None);
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/tests/rm.DelegatingHandlersTest/RelayHandlerTests.cs
where HttpMessageInvokerFactory
is as,
public static class HttpMessageInvokerFactory
{
public static HttpMessageInvoker Create(
params DelegatingHandler[] handlers)
{
return Create(null!, handlers);
}
public static HttpMessageInvoker Create(
HttpMessageHandler innerHandler,
params DelegatingHandler[] handlers)
{
if (handlers == null || !handlers.Any())
{
throw new ArgumentNullException(nameof(handlers));
}
if (handlers.Any(x => x == null))
{
throw new ArgumentNullException(nameof(handlers), "At least one of the handlers is null.");
}
var first = handlers[0];
Array.Reverse(handlers);
var current = innerHandler;
foreach (var next in handlers)
{
if (current != null)
{
next.InnerHandler = current;
}
current = next;
}
return new HttpMessageInvoker(first);
}
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/tests/rm.DelegatingHandlersTest/misc/HttpMessageInvokerFactory.cs
The reason why FooHandler
's InnerHandler
needs to be setup is because its InnerHandler
is executed is my guess in your test. That said, one could have a Delegating Handler that "short-circuits" the call, and one doesn't need its InnerHandler
.
Here is a Delegating Handler, ThrowingHandler
that throws unconditionally.
/// <summary>
/// Throws an exception.
/// </summary>
public class ThrowingHandler : DelegatingHandler
{
private readonly Exception exception;
/// <inheritdoc cref="ThrowingHandler" />
public ThrowingHandler(
Exception exception)
{
// funny, no?
this.exception = exception
?? throw new ArgumentNullException(nameof(exception));
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
throw exception;
}
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/src/rm.DelegatingHandlers/ThrowingHandler.cs
I don't need to setup its InnerHandler
in test as it's not used. :shrug: One could if they feel like to have a consistent test setup.
[Test]
public void Throws()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var throwingHandler = new ThrowingHandler(new TurnDownForWhatException());
using var invoker = HttpMessageInvokerFactory.Create(
throwingHandler);
using var requestMessage = fixture.Create<HttpRequestMessage>();
var ex = Assert.ThrowsAsync<TurnDownForWhatException>(async () =>
{
using var _ = await invoker.SendAsync(requestMessage, CancellationToken.None);
});
}
// TurnDownForWhatException is a custom exception
source: https://github.com/rmandvikar/delegating-handlers/blob/main/tests/rm.DelegatingHandlersTest/ThrowingHandlerTests.cs
Note that the TestHandler
in Ray's answer is better named as HttpStatusOkHandler
. But, one could need a handler to spoof an Http 400 instead, and so on.
public class HttpStatusOkHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
}
}
So, eventually I came up with below.
/// <summary>
/// Short-circuits with a canned http response.
/// </summary>
/// <remarks>
/// Canned response should not be disposed if used with a retry handler
/// as it's meant for multiuse. If so, consider using <see cref="ShortCircuitingResponseHandler"/>
/// instead.
/// </remarks>
public class ShortCircuitingCannedResponseHandler : DelegatingHandler
{
private readonly HttpResponseMessage response;
/// <inheritdoc cref="ShortCircuitingCannedResponseHandler" />
public ShortCircuitingCannedResponseHandler(
HttpResponseMessage response)
{
this.response = response
?? throw new ArgumentNullException(nameof(response));
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
return Task.FromResult(response);
}
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/src/rm.DelegatingHandlers/ShortCircuitingCannedResponseHandler.cs