2013-05-14

Unit Testing with WebRequest/WebResponse (and beyond)


The problem

As you work in TDD, you'll find some rough edges for Unit Testing in .NET, particularly with IO, Database and networking. If you are doing Unit Testing right, then you're interested in keeping your tests repeatable, isolated and fast, and you simply cannot read/write files, make SQL queries or send/receive network requests. Typically, you'll avoid this by keeping all your classes decoupled by interfaces, and instantiated them with a Dependency Injection framework (e.g. Castle Windsor), and finally mocking your dependencies in your unit tests with another framework (e.g. Moq). The challenge in .NET is that most of your IO/Database/Network classes do not offer interfaces, are sealed classes, and have static methods. Since this is not a new problem, there are several individuals who have come up with their solutions: build a wrapper library around the original implementations and expose it as interfaces. Two of such solutions are System.IO.Abstractions and SystemWrapper. Unfortunately, neither of these libraries includes the WebRequest/WebResponse classes (nor the FtpWebRequest/FtpWebResponse, the ones I actually needed), so I had to come up with a wrapper of my own. Since I can't share my employer's proprietary code, and I think this can be useful to others (or myself in the future), I decided to come up with my open-source library at github in my free time.

What's available (so far)

System.Net.Interfaces class diagram


Notes

Although I tried to make these classes/interfaces close to the original, there a couple of notable changes:
  • Not all methods and properties are exposed (namely asynchronous methods). WebRequest doesn't implement IDisposable.
  • Factory methods (such as WebRequest.Create) are available in an abstract factory: WebRequestFactory.
  • The library is built on .NET framework version 4.0. It could be built on version 2.0...
  • In some situations, WebRequest.GetResponse method will throw a WebException with an inner WebResponse object, instead of just returning the WebResponse object. In my opinion, this was a terrible design decision by the .NET team, but we have to live with it. I could catch that particular exception and return an IWebResponse, but I would be doing more than just wrapping...
Todo

  • Add some code samples showing how to use it
  • Extend it with other implementations (the WebClient class is the first that comes to my mind)
  • Make a NuGet package
  • Address some of the points in the "Notes" section
So, what do you think? Have you dealt with this kind of issues before? Do you have a different approach? Is this library useful at all?

Edit (2013-07-01): Unknown to me, Microsoft already solved this a couple of years ago, through the System.Web.Abstractions namespace (included with ASP.NET MVC 3.5), and more recently (.NET 4.0 onwards), included in the System.Web namespace.

4 comments:

  1. The System.Web.Abstractions does not cover FTP. Just HTTPRequest and response. I believe.

    ReplyDelete
  2. Nick: you are correct. Cool, then there is still hope for my pet project :)

    ReplyDelete
    Replies
    1. Yes. But I'd like to see how you use it for tests. I grabbed your code and included it in my project. But I'm still doing something goofy where I can't unit test. It may be because I added a static method to call the webrequestfactory and my solution did not quite pan out with mocking. I've also made some changes, like implementing iDisposable and some of the async methods. Not sure if they are correct at all. Just calling underlying base methods. If it is not dead I suppose I could fork and send a pull request.

      Delete
    2. Great, sent the pull request. Yes, I do intend to add a sample of a working example.

      Delete