Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simulate HttpRequestException for unit testing

I have an application that downloads massive amounts of pdfs from the web. From time to time, I get a HttpRequestException, contaning the message: Error while copying content to a stream.

So, I am trying to unit test my code to handle this situation. My current code for downloading is:

var request = await httpClient.GetAsync(url);

// This is where the exception would be thrown
await request.Content.ReadAsByteArrayAsync());

Now I am trying to simulate a HttpRequestException, so that I can unit test the code above, but I dont know how to do it. Can anyone please help?

Thanks in advance!

like image 250
Fernando Silva Avatar asked Nov 01 '25 10:11

Fernando Silva


2 Answers

The key here is creating an HttpContent that throws an exception:

public class ExceptionThrowingContent : HttpContent
{
    private readonly Exception exception;
    public ExceptionThrowingContent(Exception exception)
    {
        this.exception = exception;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        return Task.FromException(exception);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = 0L;
        return false;
    }
}

Once you have that, you can use something like my own mockhttp to mock the request:

var handler = new MockHttpMessageHandler();
handler.When("http://tempuri.org/url")
    .Respond(new ExceptionThrowingContent(ex));

var mockClient = new HttpClient(handler);

// pass mockHandler to your component

Now, if your component passes in HttpCompletionOption.ResponseHeadersRead when it makes the request, the exception will be thrown at await Content.ReadAsByteArrayAsync(). If not, HttpClient will attempt to buffer the response so the exception will be thrown at await HttpClient.GetAsync().

If the response is being buffered, it's realistically "impossible" for an exception to be thrown at ReadAsByteArrayAsync so there's no point in attempting to simulate it. ("impossible" outside an OutOfMemoryException)

like image 148
Richard Szalay Avatar answered Nov 03 '25 22:11

Richard Szalay


With MockHttp it is easy setup HttpClient tests in a fluent manner you can return a customized HttpContent that throws a HttpRequestException like in this example.

[TestMethod]
[ExpectedException(typeof(HttpRequestException))]
public async Task Test()
{
    var content = new ContentWithException();
    var mockHttp = new MockHttpMessageHandler();
    mockHttp.Expect("http://localhost/mypdfDownload").Respond(content);

    var client = new HttpClient(mockHttp);
    var response = await client.GetAsync("http://localhost/mypdfDownload");

    await response.Content.ReadAsByteArrayAsync();
}

private class ContentWithException : HttpContent
{
    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        throw new HttpRequestException();
    }

    protected override bool TryComputeLength(out long length)
    {
        length = 0;
        return false;
    }
}
like image 42
Ralf Bönning Avatar answered Nov 03 '25 23:11

Ralf Bönning