Unit Testing a Web Service
Dovetail sells a web services for Clarify product that consists of a SOAP flavored web service and a handy client library. To make sure all is continuously well we have NUnit integration tests that exercise the client library making calls against the web service. Our first iteration of the testing the web service involved launching Cassini and then later the development web server that comes with Visual Studio 2005 to host our web service under test. The configuring and launching of this process (WebDev.WebServer2.exe) has been fragile and prone to failure. Yesterday while setting up a new integration server I flailed for an hour trying to get the web server running. My defeat turned into victory after I found Phil Haack’s post on Using WebServer.WebDev For Unit Tests which taught me how remove our dependency on the executable web server and instead use the underlying assembly WebDev.WebHost.dll to do all the heavy lifting in-process with our tests.
Using Phil’s guidance: I grabbed the web host assembly out of the GAC and threw it into our lib folder then I was able to do a little hacking to Phil’s TestWebServer.cs. It was pretty easy to adapt it to get a web server started and hosting our web application. Here is the meat of what the class does.
public Uri Start() { //NOTE: WebServer.WebHost is going to load itself AGAIN into another AppDomain //NOTE: so we need to make sure that it is present in the binaries for the web application. File.Copy("WebDev.WebHost.dll", Path.Combine(_webRootDirectory, @"bin\WebDev.WebHost.dll"), true); //Start the internal Web Server pointing to our test webroot _webServer = new Server(_webServerPort, _webServerVDir, _webRootDirectory); webServerUrl = String.Format("http://localhost:{0}{1}", _webServerPort, _webServerVDir); _webServer.Start(); started = true; Debug.WriteLine(String.Format("Web Server started on port {0} with VDir {1} in physical directory {2}", _webServerPort, _webServerVDir, _webRootDirectory)); return new Uri(webServerUrl); }
Once I got the TestWebServer class working for my needs it was easy to update our test fixture. I created a test fixture base class to handle starting the web server and configuring the client to the web service.
[TestFixture] public class WebservicesTest : ClarifyBaseTest { private TestWebServer _webServer; private ClarifyApplicationWS _clarifyApplicationWS; public ClarifyApplicationWS ClarifyApplicationWS { get { return _clarifyApplicationWS; } } protected override void OnTestFixtureSetup() { string webRootDirectory = ConfigurationManager.AppSettings["ClarifyWebServices.root.dir"]; _webServer = new TestWebServer(webRootDirectory); Uri serverUrl = _webServer.Start(); _clarifyApplicationWS = new ClarifyApplicationWS(serverUrl.AbsoluteUri); } protected override void OnTestFixtureTeardown() { if (_webServer != null) { _webServer.Stop(); } } }
Now all the tests run without needing to launch a web server. Life is good. I went over to the integration server did an svn update and all web service tests ran without further complication. Decoupling our build from the complexity of launching a separate process feels great. Thank you Phil for the excellent and informative post (Subscribed).