Mark Gilbert's Blog

Science and technology, served light and fluffy.

Queue them up! Unit testing HttpClient

Early in my current project, I was looking for a way to test some logic that relied on HttpClient.PostAsync.  I was writing a routine that would process items coming off a queue that was exposed via a series of REST services.  Subscribing to the queue, pulling messages down, and cleaning the subscription up once the queue was empty all required calls to the services, and in most cases several calls.  Ideally I’d be able to mock the calls to HttpClient.PostAsync out, but I can’t because that method isn’t virtual.  The usual way to handle methods like that is to build an interface that I CAN mock out, and then build a wrapper-implementation of that interface which calls HttpClient.PostAsync.  My unit tests would use a mock of that interface, and the real code would use my wrapper.  Adding interfaces adds flexibility to a piece of software, at the cost of readibility and maintainability, so before I went that route I wanted to see if there was a better way.

I came across this post by Gabriel Perez where he shows how to build a fake handler that returns an “injected” response when PostAsync is invoked.  (He credits Glenn Bock for the original source.)  This “russian doll model” as he calls it looked very promising.  In fact, I thought I could even extend it to inject the responses from a queue, allowing me to line them for a specific scenario, and then test my code against it.

I decided that a simple list would suffice for the queue.  FakeHandler would just keep track of which responses it had already handed out:

// FakeHandler, adapted from: http://perezgb.com/2012/02/21/web-api-testing-with-httpclient
        public class FakeHandler : DelegatingHandler
        {
            public List<HttpResponseMessage> Responses { get; set; }
            private int _Index;
public FakeHandler() : base()
            {
                this.Responses = new List<HttpResponseMessage>();
                this._Index = -1;
            }

            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
this._Index++;
                if (this._Index >= this.Responses.Count) { return Task.Factory.StartNew(() => (HttpResponseMessage)null); }
                return Task.Factory.StartNew(() => Responses[this._Index]);
            }
        }

Here is how a test would look using this structure:

        [Test]
        public void SomeTest()
        {
            HttpResponseMessage ConfiguredResponse;
            HttpClient ConfiguredClient;
            FakeHandler NewHandler;
            QueueProcessor MyProcessor;

            // Configure the response.  Set properties (headers, etc.) as needed.
            ConfiguredResponse = new HttpResponseMessage();

            // Build a handler around this response
            NewHandler = new FakeHandler() { InnerHandler = new HttpClientHandler() };
            NewHandler.Responses.Add(ConfiguredResponse);

            // Create a client object around this handler
            ConfiguredClient = new HttpClient(NewHandler);

            // Pass in the pre-configured client to the class under test (dependency injection)
            MyProcessor = new QueueProcessor(ConfiguredClient);

            // Test the method
            var Results = MyProcessor.DoSomething();

            // TODO: Evaluate the results
        }

For “real” use, I would simply pass in a new HttpClient() object to the QueueProcessor constructor.  This all worked quite well to mock out the responses, but my tests could only evaluate what came out at the very end.  I also wanted to make sure the individual responses were being handled properly.

I could have simply injected a set of Asserts into FakeHandler.SendAsync, but I wanted to build it so it could potentially handle completely different tests each time.  To do that, I added an event handler called “OnEntryVerificationMethod”.  This became a property of the FakeHandler class that I could define in my unit test, which would be run at a known point in the execution (on entry), and would have access to both the current and previous responses (see bolded additions below).

// Define the prototype for the delegate that will perform our validation
        public delegate void VerifyOnEntry(HttpRequestMessage CurrentRequest, HttpResponseMessage PreviousResponse);

        // FakeHandler, adapted from: http://perezgb.com/2012/02/21/web-api-testing-with-httpclient
        public class FakeHandler : DelegatingHandler
        {
            public List<HttpResponseMessage> Responses { get; set; }
            private int _Index;

            // Define a delegate that allows me to pass in a custom function at runtime
            public VerifyOnEntry OnEntryVerificationMethod;

            public FakeHandler() : base()
            {
                this.Responses = new List<HttpResponseMessage>();
                this._Index = -1;
            }

            protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
            {
                // If a custom function was defined, invoke it, passing in the parameters to be verified.
                if (OnEntryVerificationMethod != null)
                {
                    OnEntryVerificationMethod(request, (this._Index < 0 ? (HttpResponseMessage)null : Responses[this._Index]));
                }

                this._Index++;
                if (this._Index >= this.Responses.Count) { return Task.Factory.StartNew(() => (HttpResponseMessage)null); }
                return Task.Factory.StartNew(() => Responses[this._Index]);
            }
        }

I could define this in my unit test using an anonymous function:

            int NumberOfMessagesProcessed = 0;
            NewHandler.OnEntryVerificationMethod = (CurrentRequest, PreviousResponse) =>
            {
                NumberOfMessagesProcessed++;

                if (PreviousResponse == null) { return; }

                switch (NumberOfMessagesProcessed)
                {
                    case 1:
                        // TODO: Perform tests for the first response processed
                        break;

                    case 2:
                        // TODO: Perform tests for the second response processed
                        break;
                }
            };

Now I had a window into FakeHandler’s execution, and could inspect anything I needed.

If HttpClient.PostAsync was virtual (and therefore directly mockable), it would have made testing one call far easier, but trying to simulate multiple calls in a row would probably have pushed me into this structure anyway.

You could argue that in lining up multiple responses like this I’m trying to test too much; that what I should be doing is setting up everything for a given scenario (e.g., responses have already come back as “fail” twice), and then pass in the one I really want to test (the third attempt is a success).  However, I think that would result in a more complicated code base because I would have to make several internal properties available to my test (like the number of times an HttpClient.PostAsync call results in a failure), or make more extensive use of interfaces, or both.  Lining up a series of responses, and then testing that greater scenario, is a more straightforward approach.

Sometimes, I will totally trade having code that is awesomely-flexible and unit test-pure for code that I can more easily get my head wrapped around.

Advertisements

September 5, 2013 - Posted by | Visual Studio/.NET, Web API

Sorry, the comment form is closed at this time.

%d bloggers like this: