3

How I can mock HttpWebResponse with the property CharacterSet for unit test? In my code I am checking for HttpWebResponse.CharacterSet like below. I could mock HttpWebResponse but getting null reference exception while getting the property "CharacterSet". Please note I cannot setup "CharacterSet" in the mock as it is a read-only and non virtual property. I am using .Net 4.6.1

public string ReadResponse(HttpWebResponse response)
    {
        var encoding = (response.CharacterSet == null || response.CharacterSet == "") ? Encoding.UTF8 : Encoding.GetEncoding(response.CharacterSet);
        using (var stream = response.GetResponseStream())
        {
            var reader = new StreamReader(stream, encoding);
            var responseString = reader.ReadToEnd();
            return responseString;
        }
    }

And the mock I have wriiten for HttpWebReponse is,

public static HttpWebResponse CreateRequestWithResponse(string responseContent)
    {

        var response = new Mock<HttpWebResponse>(MockBehavior.Loose);
        var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(responseContent));
        response.Setup(c => c.StatusCode).Returns(HttpStatusCode.OK);
        response.Setup(c => c.ContentType).Returns("text/xml;charset=\"utf-8\"");
        response.Setup(c => c.GetResponseStream()).Returns(responseStream);

        var request = new Mock<HttpWebRequest>();
        request.Setup(a => a.ContentType).Returns("text/xml;charset=\"utf-8\"");
        request.Setup(s => s.GetResponse()).Returns(response.Object);

        return response.Object;
    }

This is the error I am getting while getting "CharacterSet",

enter image description here

Vijayanath Viswanathan
  • 8,027
  • 3
  • 25
  • 43

2 Answers2

2

Knowing the source code you can manually set the private properties of the HttpWebResponse class that are used in the Get-Handler of the CharacterSet property:

public static HttpWebResponse CreateRequestWithResponse(string responseContent)
{
    var response = new Mock<HttpWebResponse>(MockBehavior.Loose);
    var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(responseContent));
    response.Setup(c => c.StatusCode).Returns(HttpStatusCode.OK);
    response.Setup(c => c.ContentType).Returns("text/xml;charset=\"utf-8\"");
    response.Setup(c => c.GetResponseStream()).Returns(responseStream);

    HttpWebResponse result = response.Object;

    // Set private field behind CharacterSet property
    var prop = typeof(HttpWebResponse).GetField("m_CharacterSet", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    prop.SetValue(result, "utf-8");

    // Set private field used in CharacterSet getter
    prop = typeof(HttpWebResponse).GetField("m_HttpResponseHeaders", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    prop.SetValue(result, new WebHeaderCollection());

    return result;
}

This depends on the internal implementation. So, if Microsoft changes the internal implementation of the HttpWebResponse class, your tests will be broken.

NineBerry
  • 26,306
  • 3
  • 62
  • 93
1

I did further research into this, and it seems that you're not going to be able to do what you want. The issue is that the CharacterSet property derives itself using the headers collection. While you can override the Headers property, the CharacterSet property looks directly at the field. The only way the header collection field is set to a non-null value is via an internal constructor which copies it from something called cordata.

The result is that there's no safe way to mock the CharacterSet. I suppose you could use reflection to find the field and set it that way, but that seems like it wouldn't be a good idea as the implementation could change and break your test.

Andy
  • 8,432
  • 6
  • 38
  • 76