Mocking the Unmockable

I last wrote about the process involved in mocking the System.Net.WebRequest abstract class to provide a way to return a WebResponse:

public static FakeWebResponse FakeResponse { get; set; }
public override WebResponse GetResponse()
{
  return (HttpWebResponse)FakeResponse;
}

You can also implement the BeginGetResponse call using Delegate.Invoke if you need to. From there, you implement your WebResponse fake:

public class FakeWebResponse : System.Net.WebResponse
{
  public FakeWebResponse()
  {
    ResponseHeaders = new WebHeaderCollection();
    StatusCode = HttpStatusCode.OK;
  }
  public WebHeaderCollection ResponseHeaders { get; set; }
  public override WebHeaderCollection Headers
  {
    get { return ResponseHeaders; }
  }
  public HttpStatusCode StatusCode { get; set; }
  // etc. Trivially implement other WebResponse members
}

This is all well and good, except that when working with HTTP, there’s one critical piece of information that’s not part of the HttpResponse class: the StatusCode member. In fact, most code I’ve seen for working with WebResponse looks like this:

pre(code)HttpWebResponse r = (HttpWebResponse)request.GetResponse();

Developers rarely bother with the abstraction to WebResponse because you almost always need to know what the HTTP status for the response is.

So this is where it gets hairy, and when I embarked on this task, I began to wonder if I wasn’t better off just giving up and using Cassini to spin up an ASP.NET server in process and use plain old HttpWebRequest. But that strategy is fraught with pain, suddenly tying you to the headache of keeping stuff on the file system, configuring a big object, maintaining out of process communications, tracking dynamic port assignments… it’s a lot of work just to get a server, and then you have to write ASPX or ASHX pages for every scenario you want to test… and then things like handling network-level errors, DNS lookup failures, or timeouts are still unsolved problems. There must be a way to do it.

The answer is in the cast operator. What we need is a way to create a HttpWebResponse if the caller actually needs one. The problem is, the constructor in question is internal, and to make matters worse, it uses several internal objects as arguments. So the short story is, it’s a pain to create an HttpWebResponse. The long story is… well, (sigh)…

public static explicit operator HttpWebResponse(FakeWebResponse f)
{
  HttpWebResponse result = null;
  Type crdType = typeof(HttpWebResponse).Assembly.GetType("System.Net.CoreResponseData");
  ConstructorInfo cons2 = crdType.GetConstructor(new Type[] { });
  object crd = cons2.Invoke(new object[] { });
  FieldInfo fi = crdType.GetField("m_ConnectStream");
  fi.SetValue(crd,f.ResponseStream);
  fi = crdType.GetField("m_ResponseHeaders");
  fi.SetValue(crd, f.ResponseHeaders);
  fi = crdType.GetField("m_StatusCode");
  fi.SetValue(crd,f.StatusCode);
  Type khvType = typeof(HttpWebResponse).Assembly.GetType("System.Net.KnownHttpVerb");
  ConstructorInfo khvTypeInit = khvType.TypeInitializer;
  FieldInfo getField = khvType.GetField("Get", BindingFlags.NonPublic | BindingFlags.Static);
  object getVal = getField.GetValue(null);
  ConstructorInfo cons = typeof(HttpWebResponse).GetConstructor(
                BindingFlags.NonPublic | BindingFlags.Instance,
                null,
                new [] { typeof(Uri),
                    khvType,
                    crdType,
                    typeof(String),
                    typeof(bool),
                    typeof(DecompressionMethods) },
                    null);
  return (HttpWebResponse)cons.Invoke(
                new [] { new Uri("http://localhost"), getVal, crd, "text/html", false, DecompressionMethods.None });
}

One funny thing I encountered was that I had to invoke the cast in the GetResponse() method rather than rely on the cast in the calling code – I found that my cast operator was never called, presumably because the compiler was creating a conversion from a WebResponse to HttpWebResponse. An explicit cast in GetResponse solves that:

pre(code).public override WebResponse GetResponse()
{ return (HttpWebResponse)FakeResponse;
}

Of course, this code comes with all the caveats that come with poking around in internal code: it can change in a future release, you’re mucking around in code you don’t own and Microsoft makes no promises about, etc. But it does work, and using this technique gives you a way to test that last bit of code that is going to hit all those network errors, DNS errors, timeouts, and HTTP Status codes that you need to handle.

I’m told that you could just spend the money on TypeMock Isolator and have all this work done for you, and that kind of power may well be worth the cost – provided you don’t use it as a crutch to avoid addressing design problems in code you control. But if you want a way to do it on the cheap, the cast operator and a bit of reflection will get you there.

— Gordon Weakliem

---

Comment

Commenting is closed for this article.