1

I have seen the following questions already:

These do not help me because I need:

  1. To test existing code which uses WebRequest/WebResponse directly, with no interfaces
  2. The existing code depends on HttpWebRequest / HttpWebResponse, so I can't use my own derived WebRequest and WebResponse classes.

I know about WebRequest.RegisterPrefix, and have a successful unit test which proves that it works (once you get the prefix right). But that test simply tests WebRequest and WebResponse.

I tried the following, but it gives a compile error (not a warning):

public class MockWebRequest : HttpWebRequest
{
    public MockWebRequest()
    // : base(new SerializationInfo(typeof (HttpWebRequest), new FormatterConverter()), 
    //        new StreamingContext())
    {
    }
}

With the two lines commented-out, I get a compile error:

error CS0619: 'System.Net.HttpWebRequest.HttpWebRequest()' is obsolete: 'This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.'

When I uncomment those lines, I get a 618 warning, but the code crashes at runtime:

System.Runtime.Serialization.SerializationException: Member '_HttpRequestHeaders' was not found.
   at System.Runtime.Serialization.SerializationInfo.GetElement(String name, Type& foundType)
   at System.Runtime.Serialization.SerializationInfo.GetValue(String name, Type type)
   at System.Net.HttpWebRequest..ctor(SerializationInfo serializationInfo, StreamingContext streamingContext)
   at UnitTests.MockWebRequest..ctor(String responseJson)
   at UnitTests.MockWebRequestCreator.Create(Uri uri)
   at System.Net.WebRequest.Create(Uri requestUri, Boolean useUriBase)
   at System.Net.WebRequest.Create(Uri requestUri)
   at code under test

I'm at a loss about how to proceed with this (other than the default, which is to just punt on these unit tests and wish the code had been implemented with testing in mind).

I could probably get "down and dirty" and do something nasty with ISerializable, or something like that, but this brings up the third requirement:

3. The code must be maintainable, and must not be too much more complex than the code being tested!

OBTW, I can't use TypeMock for this, and in fact there's a bit of a time limit. The project is almost done, so a new, purchased mocking framework is out of the question.

Community
  • 1
  • 1
John Saunders
  • 160,644
  • 26
  • 247
  • 397

2 Answers2

0

I generally prefer to use a mocking framework like MOQ when mocking asp.net singletons like httpcontext.

There are other frameworks out there for mocking, but I find MOQ to be very flexible and intuitive

https://github.com/Moq/moq4/wiki/Quickstart

You can do something similar to this

http://www.syntaxsuccess.com/viewarticle/how-to-mock-httpcontext

Another approach is to abstract the call to httpcontext through a virtual method: Similar to this technique: http://unit-testing.net/CurrentArticle/How-To-Remove-Data-Dependencies-In-Unit-Tests.html

TGH
  • 38,769
  • 12
  • 102
  • 135
  • Unfortunately, there's no equivalent of `HttpContextBase` for `HttpWebRequest`. There's no `HttpWebRequestBase` class. – John Saunders Jan 09 '14 at 04:14
  • Ok. I added another approach that I sometimes use – TGH Jan 09 '14 at 04:15
  • 1
    I'm afraid that won't help, either. As I said in the question, I can't make changes to the code under test. – John Saunders Jan 09 '14 at 04:16
  • Ok, tricky.. Another idea I guess could be to do a full integration test where you hit your endpoint (controller etc) with a regular https request instead. IIS express can be launched from the command line in the test. This is maybe not ideal, but it would at least let you assert changes caused by the code under test. However, it's not a conventional unit test though – TGH Jan 09 '14 at 04:24
  • 1
    I have a crazy idea about _delegating_ to the real `HttpWebRequest` and `HttpWebResponse`. I would continue to register my `test://localhost/` prefix, but the registered handler would _delegate_ to the real classes, but hit something like `http://localhost/DUMMY_SERVICE`. I would have to use `HttpListener` to create the dummy service. Note how this is becoming more complex than the code under test... – John Saunders Jan 09 '14 at 04:26
0

I just ran into the same problem. Frankly, the way Microsoft has set this up doesn't make much sense - in WebRequest.Create they have an abstract factory, but they neither provide interfaces for HttpWebRequest and HttpWebResponse that we can implement, nor do they provide a protected constructor that we can use to inherit from these classes.

The solution I have come up with requires some minor changes to the main program, but it works. Basically, I am doing what Microsoft should have done.

  1. Create interfaces IHttpWebRequest and IHttpWebResponse
  2. Create mock classes which implement the interfaces
  3. Create wrappers for HttpWebRequest and HttpWebResponse which implement the interfaces
  4. Replace calls to WebRequest.Create with my own factory method that returns the wrappers or the mocks, as needed

Depending on how your code is structured, the above may be relatively trivial to implement, or it may require making changes throughout your project. In my case, we have all of our HTTP requests going through a common service, so it hasn't been too bad.

Anyway, probably too late for your project, but maybe this will help someone else down the road.

matteagar
  • 1
  • 1