Quick link: download
In my work at Semingo I often encounter situations where it's impossible to unit- or integration-test a component without accessing the web. This happens in one of two cases: either the component itself is web-centric and makes no sense in any other context, or I simply require an actual web server to test the components against.
Since I firmly believe that tests should be self-contained and rely on external resources as little as possible, a belief which also extends to integration tests, I wrote a quick-and-dirty pluggable web server based on the .NET HttpListener class. The unit-tests for the class itself serve best to demonstrate how it's used; for instance, the built-in HttpNotFoundHandler returns 404 on all requests:
[Test]
[ExpectedException( typeof( WebException ) )]
[Description( "Instantiates an HTTP server that returns 404 on all " +
"requests, and validates that behavior." )]
public void VerifyThatHttpNotFoundHandlerBehavesAsExpected()
{
using ( LightweightWebServer webserver =
new LightweightWebServer( LightweightWebServer.HttpNotFoundHandler ) )
{
WebRequest.Create( webserver.Uri ).GetResponse().Close();
}
}
The web server randomizes a listener port (in the range of 40000-41000, although that is easily configurable) and exposes its own URI via the
LightweightWebServer.Uri property. By implementing
IDisposable the scope in which the server operates is easily defined. Exceptions thrown from within the handler are forwarded to the caller when the server is disposed:
[Test]
[ExpectedException( typeof( AssertionException ) )]
public void VerifyThatExceptionsAreForwardedToTestMethod()
{
using ( LightweightWebServer webserver = new LightweightWebServer(
delegate { Assert.Fail( "Works!" ); } ) )
{
WebRequest.Create( webserver.Uri ).GetResponse().Close();
}
}
The handlers themselves receive an
HttpListenerContext, from which both request and response objects are accessible. This makes anything from asserting on query parameters to serving content trivial:
[Test]
public void VerifyThatContentHandlerReturnsValidContent()
{
string content = "The quick brown fox jumps over the lazy dog";
using ( LightweightWebServer webserver = new LightweightWebServer(
delegate( HttpListenerContext context )
{
using ( StreamWriter sw = new StreamWriter( context.Response.OutputStream ) )
sw.Write( content );
} ) )
{
string returned;
using ( WebResponse resp = WebRequest.Create( webserver.Uri ).GetResponse() )
returned = new StreamReader( resp.GetResponseStream() ).ReadToEnd();
Assert.AreEqual( content, returned );
}
}
We use this class internally to mock anything from web services to proxy servers. You can grab the class sources here -- it's distributed under a Creative Commons Public Domain license, so you can basically do anything you want with it. If it's useful to anyone, I'd love to hear comments and suggestions!