Web Api - Testing with HttpClient

The Web Api is a project that has been on my radar since I first saw Glenn Block’s presentation last year on MIX. I was only recently though, when we decided to really take on REST at work that I really started looking into with in depth.

One aspect that caught my attention was the HttpClient. I’ve been using RestSharp for a while and I have to say that I am really happy with it. I still wanted to test the new HttpClient and see how it compared to RestSharp.

So let’s say I want to call a super duper service that returns a Guid. Here is a VERY simple example of how I would implement a client using RestSharp:

public class GuidClient
{
    private readonly IRestClient _client;

    public GuidClient(IRestClient client)
    {
        _client = client;
    }

    public string Execute()
    {
        var request = new RestRequest();

        RestResponse response = _client.Execute(request);

        if (response.StatusCode != HttpStatusCode.OK)
        {
            throw new Exception("Invalid response");
        }
        
        return response.Content;
    }
}

As I said, it's a very simple example. The important part here is that if I get a valid response I want to return the content (which should be the GUID), otherwise I want to throw an Exception. Here are the tests:

[TestFixture]
class GreetingClientTest
{
    [Test]
    public void Throws_exception_if_response_not_OK()
    {
        var mock = new Mock<IRestClient>();
        mock.Setup(x => x.Execute(It.IsAny<IRestRequest>()))
            .Returns(new RestResponse {StatusCode = HttpStatusCode.BadRequest});

        var client = new GuidClient(mock.Object);
        Assert.Throws<Exception>(() => client.Execute());
    }

    [Test]
    public void Returns_content_if_response_is_OK()
    {
        string content = Guid.NewGuid().ToString();
        var mock = new Mock<IRestClient>();
        mock.Setup(x => x.Execute(It.IsAny<IRestRequest>()))
            .Returns(new RestResponse
                         {
                             StatusCode = HttpStatusCode.OK,
                             Content = content
                         });

        var client = new GuidClient(mock.Object);
        var result = client.Execute();
        Assert.AreEqual(content, result);
    }
}

It’s easy to notice how simple the RestSharp abstractions make our job of testing. Mock the IRestClient to return the desired RestResponse and it's good to go.

When creating my first client using the HttpClient I wanted to pass the HttpClient as a constructor parameter in the same manner I did with IRestClient. That’s when I noticed that the HttpClient doesn’t implement any interfaces other than IDisposable. Hum, no interfaces? So how can I mock this thing? Good thing Glenn Block was ready to help on twitter:

“@gblock: @perezgb @howard_dierking with web api you can pass a fake message handler to the client to test.”

So Glenn also sent me the code from one of his talks where he creates a fake handler in order to help testing with the HttpClient. So taking his code I created an HttpClient version of my service and tests:

public class GuidHttpClient
{
    private readonly HttpClient _client;

    public GuidHttpClient(HttpClient client)
    {
        _client = client;
    }

    public string Execute()
    {
        var request = new HttpRequestMessage { RequestUri = new Uri("http://localhost/guidservice") };
        Task<HttpResponseMessage> task = _client.SendAsync(request);
        HttpResponseMessage response = task.Result;
        if (response.StatusCode != HttpStatusCode.OK)
        {
            throw new Exception("Invalid response");
        }
        return response.Content.ReadAsStringAsync().Result;
    }
}

And here are the tests:

[TestFixture]
public class GuidHttpClientTest
{
    [Test]
    public void Throws_exception_if_response_not_OK()
    {
        var response = new HttpResponseMessage(HttpStatusCode.BadRequest);
        var httpClient = new HttpClient(new FakeHandler
                                            {
                                                Response = response,
                                                InnerHandler = new HttpClientHandler()
                                            });

        var client = new GuidHttpClient(httpClient);
        Assert.Throws<Exception>(() => client.Execute());
    }

    [Test]
    public void Returns_content_if_response_is_OK()
    {
        string content = Guid.NewGuid().ToString();
        var response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new StringContent(content);

        var httpClient = new HttpClient(new FakeHandler
        {
            Response = response,
            InnerHandler = new HttpClientHandler()
        });

        var client = new GuidHttpClient(httpClient);
        string result = client.Execute();
        Assert.AreEqual(content, result);
    }
}

And this is the fake message handler:

public class FakeHandler : DelegatingHandler
{
    public HttpResponseMessage Response { get; set; }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
                                                           CancellationToken cancellationToken)
    {
        if (Response == null)
        {
            return base.SendAsync(request, cancellationToken);
        }

        return Task.Factory.StartNew(() => Response);
    }
}

Ok, so mission accomplished! I was able to write my service and tests using the HttpClient. One thing I really like is the russian doll model, kinda like the one you can find on FubuMVC, that the DelegatingHandler makes possible. On the other hand, I still like my RestSharp tests better, they seem cleaner but maybe that’s just me. It's the way I've been writing code for a while and I feel comfortable with it. Some times though, we have to push ourselves out of our comfort zone and try different things, right?

Please take all this with a grain of salt as I don’t consider myself to be any kind of expert in the ASP.NET Web Api. Also this is only my first stab at the HttpClient, I plan to keep going with my tests. I'll let you know if I come across anything interesting ;-)

Bringing Code Katas to our User Group

 Last week we had our first Kata Night at our user group. I'd like to share a little of our experience with everyone as well as address a few points that were raised by our members.

The idea to start doing Code Katas in our meetings came from an excellent workshop Dave Nolf (@dnolf) and I attended at Codemash this year. This workshop was called Software Craftsmanship and was led by Steve Smith (@ardalis) and Brendan Enrick (@brendoneus). Throughout the day we did several katas which gave us the chance to exercise TDD and pair programming.

As soon as I got back to Toronto I knew I had to start working on doing this kind of exercises in our user group. Right away I sent an email to the guys and everybody seemed to be excited about the idea. The great thing is that we even got some new members to show up just to try the katas.

What we did

We worked on Uncle Bob's Prime Factors Kata. I chose this one because it's one of the shortest one we could do. It was also very easy for me to do the entire kata in a few minutes and show everyone how to approach the problem from the TDD perspective. Additionally, the fact that Uncle Bob has the slides with the entire solution done step by step was very helpful, so if anyone wanted to go over it later it would be very easy.

We first tried to do the kata individually and purposely not using tests. The idea was to give the guys a feeling of how hard it could be implement something without tests while trying to tackle the entire problem at once instead of incrementally. We then proceeded to do two more iterations, this time in pairs and using TDD. We switched the partners at each iteration and we also threw away the entire code from the previous attempt.

How it went

I had a feeling people were having a little difficulty trying to figure out how to do the tests, and how much to implement to make each test pass. Another factor was that no one had ever done this kind of exercise before. There might have been some hesitation and discomfort in coding in front of someone else for the first time.

On the other hand I could feel that there was a lot of excitement. At the final iteration people were really loosening up, talking more and clearly having more fun. Everyone was really stoked about solving the problem. When the time ran out everyone wanted a few more minutes to finish their implementation. Of course we didn't add any extra time, that's the whole point :-)

Final impressions

Talking to the guys afterwards I could really tell that the experience had been really positive. Some of them were saying that they could finally see themselves using unit testing in real life as they could now have some guidance on how to do it.

Even though the experience was mostly positive I'd like to address some of the concerns the guys had:

1. There was not enough time to finish the exercise, which left me a little frustrated; I wish we had more time.

The whole point of the katas is that you train yourself to complete the challenge in time. If you can finish a kata you've never done before in your first attempt and in time then either your skills are very sharp or the time allotted was too large. As you keep doing your kata, you will become faster and your code will improve. Don't be upset if you can't get everything done, just keep on exercising. 

2. It was fun doing TDD and pair programming but I don't think it's practical to do it at work, it takes too long.

When you first start doing TDD it really seems like it takes a long time to come up with your tests. What you have to remember is that you will get better, writing tests is an art that requires experience. Once you get into the hang of it you will write your tests much quicker. You will also have a better feeling of what to test and how to test it.

More important yet, you will be writing better code. To make your code testable you will be forced to create smaller and more decoupled classes. You will use interfaces more than ever before. And whenever the need arises for the code to be refactored you will do it quicker than  ever before as now you have tests to enforce that nothing has been broken in the process.

3. It was hard coming up with the minimum possible code to make the test pass. I had the solution pretty much done after the second test.

Again, test is an art. If you're not used to doing TDD your brain is trained to reach the entire solution at once, that's the way people have been writing software for a long time. TDD will help you tackle the problem in an incremental way, doing only the absolute necessary to make the test pass. The problem is finding what that minimum is and, again, that is why we are doing the katas. Through repetition and with the help of more experienced TDD programmers we will be able to find the balance. 

Thanks

I'd like to thank everyone who came to the meeting and really pushed themselves into trying something new. I already have some katas in mind for our next meeting. See you then.