Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq: How to test a class using Nunit with an internal HttpClient?

I run my tests inside nUnit and normally I can mock out dependencies and have then Return certain values or throw errors.

I have a class that as an internal HttpClient and I would like to test the class, what are my options.

here is my code, its not complete so as not to flood the message. As you can see I am using the HttpClient internally and not injected as a dependency. The class throws a number of custom exceptions, I would like to Moq these otherwise I need to pass REAL username and passwords that would give me the status codes i required to throw the exceptions.

Anyone have an ideas? If I can't mock the httpclient then i can never test my class that it raises exceptions.

Do I really have to change HttpClient to a dependency on the constructor ?

public bool ItemsExist(string itemValue)
{

    var relativeUri = string.Format(UrlFormatString, itemValue.ToUpper());

    var uri = new Uri(new Uri(this.baseUrl), relativeUri);

    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", this.encodedCredentials);
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));

        var response = client.GetAsync(uri).Result;

        switch (response.StatusCode)
        {
            case HttpStatusCode.Unauthorized:
                // DO something here
                throw new CustomAuthorizationException();

            case HttpStatusCode.Forbidden:
                throw new CustomAuthenticationException();

        }

        return true;
like image 276
Martin Avatar asked Sep 10 '13 15:09

Martin


2 Answers

Let me suggest a bit easier solution, without a need to abstract/wrap httpclient, that i believe works perfectly with mocking frameworks.

You need to create a class for fake HttpMessageHandler, like here:

public class FakeHttpMessageHandler : HttpMessageHandler
{
    public virtual HttpResponseMessage Send(HttpRequestMessage request)
    {
        throw new NotImplementedException("Rember to setup this method with your mocking framework");
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        return Task.FromResult(Send(request));
    }
}

Such created HttpMessageHandler can be used when instantiating HttpClient:

var msgHandler = new Mock<FakeHttpMessageHandler>() { CallBase = true };
var fakeclient = new HttpClient(msgHandler.Object);

And you can setup methods (here using Moq):

msgHandler.Setup(t => t.Send(It.Is<HttpRequestMessage>(
            msg =>
                    msg.Method == HttpMethod.Post &&
                    msg.RequestUri.ToString() == "http://test.te/item/123")))
                    .Returns(new HttpResponseMessage(System.Net.HttpStatusCode.NotFound));

You can now user fakeclient instead when necessary.

like image 78
ITmeze Avatar answered Sep 26 '22 19:09

ITmeze


You can't unit test it like that. It's like you mentioned: HttpClient is a dependency, and as such, it should be injected.

Personally, I would create my own IHttpClient interface, implemented by HttpClientWrapper, which wraps around the System.Net.HttpClient. IHttpClient would then be passed as a dependency to your object's contructor.

As follows, HttpClientWrapper can't be unit tested. I would, however, write a couple of integration tests to make sure the wrapper is well written.

Edit:

IHttpClient doesn't have to be a "valid" interface for HttpClient. It only has to be an interface that suits your needs. It can have as many or as few methods as you want.

Picture this: HttpClient allows you to do many things. But in your project, you're only calling the GetAsync(uri).Result method, nothing else.

Given this scenario, you would write the following interface and implementation:

interface IHttpClient
{
    HttpResponseMessage Get(string uri);
}

class HttpClientWrapper : IHttpClient
{
    private readonly HttpClient _client;

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


    public HttpResponseMessage Get(string uri)
    {
        return _client.GetAsync(new Uri(uri)).Result;
    }
}

So, as I stated previously, the interface only has to suit your needs. You don't have to wrap around the WHOLE HttpClient class.

Obviously, you would then moq your object like this:

var clientMock = new Mock<IHttpClient>();
//setup mock
var myobj = new MyClass(clientMock.object);

And to create an actual object:

var client = new HttpClientWrapper(new HttpClient());
var myobj = new MyClass(client );

Edit2

OH! And don't forget that IHttpClient should also extend the IDisposable interface, very important!

like image 30
dcastro Avatar answered Sep 25 '22 19:09

dcastro