So I had mentioned I'd be working with NUnitASP and that id'd post some comments on my experience. I used it to create a comprehensive test for a relatively complex ASP.NET WebControl. It has proved to be a tremendous help. I've gone through the code extensively to see how it works and it's quite interesting. First let me explain the details of NUnitASP...
To use NUnitASP code is written in normal NUnit test fixtures. By inheriting from NUnitASP's WebFormTestCase, your test code has access to an HttpClient object ("Browser") for requesting *and* posting back pages of an ASP.NET site. The response of the site is interpreted as XML using code from the SgmlReader project. The SgmlReader takes typical HTML (i.e. not well formed XML) as input and turns it into well formed XML as it is read by exposing it as a System.Xml.XmlReader, which in turn allows NUnitASP to provide the HTML page as an XmlDocument. Once a rendered ASP.NET page is available as an XmlDocument, the rest of NUnitASP's inner workings come together quickly.
Each ASP.NET control has a "Tester" class that mimics the equivalent ASP.NET control by having the same properties. For example the ButtonTester has a Text property that is used just like the ASP.NET's Button class, and the ListControlTester has Items, SelectItem, and SelectIndex properties and so on. Each tester can be used from your test case just like the equivalent ASP.NET control. Except that the tester is manipulating the ASP.NET control from the browser's perspective. Except instead of parsing the returned HTML and rendering it on screen like a browser, the parsed HTML is used by testers objects to find the output of named ASP.NET controls by the ASP.NET control's ID (and potentially the ID of its ASP.NET containers). This is possible because ASP.NET controls write their control name used on the server into the rendered HTML as the ID attribute of the HTML element containing it's rendered output. Once control's HTML element is found, the attributes and InnerXml of the element are up to the specific Tester implementation to interpret and provide through convenient properties as necessary.
Let's take the ASP.NET ListBox control for examle. On the server it might look like this:
<asp:listbox id="ListBox1" runat="server">
And on the client the same control renders as:
<SELECT id="ListBox1" size="4" name="ListBox1">
Since NUnitASP provides the rendered web page as an XmlDocument, our ListBox control's rendering on the client could be found using XPath of something like:
XmlElement listBoxElement = xmlDoc.GetElementById("ListBox1");
To return the item(s) rendered by the ListBox control to the browser, you could use something like:
XmlNodeList items = listBoxElement.SelectNodes("option");
To get the Item set to be selected by ASP.NET you cold enumerate the nodes in items, and find the one with a selected attribute. Or even use some xpath query.
All this low level searching for elements as shown above is abstracted by a Tester class created for each of the intrinsic ASP.NET controls. So instead your test case's code would be more like:
WebForm webForm = new WebForm(Browser);
ListBoxTester listBox1 = new ListBoxTester("ListBox1", webForm);
Assert("first listbox item not selected!", listBox1.Items.Selected);
I needed to test a custom ASP.NET control that was significantly more complex than any of the intrinsic ASP.NET controls. So I created a custom Tester object for the control to abstract the complex xpath queries that were necessary to decipher the significant items in the rendering of the control. In the end, NUnitASP made the test cases quite compact, clear and very comprehensive. I covered my unit tests to make sure the unit tests were comprehensive and was able to make allot of progress in just a couple of days.
I only had two real problems. One was the base ControlTester object in NUnitASP had an internal constructor, so my custom Tester couldn't inherit from it and thus work with the rest of NUnitASP. Howerver, since NUnitASP is open source, I got the source out, made a quick change, recompiled and solved that one. The other obstacle was that I the web control I'm testing renders images, IFrames and even ActiveX objects that all have links back to dynamically generated content on the server. I needed to make another request for these objects based on what was rendered by the web control to verify the dynamically generated content. NUnitASP didn't have a way to do this, so I used System.NET to create a quick utility that given a TestCase containing the URL to the page currently being tested could resolve the relative links, and download these dynamic objects and make them available in the test for further validation. It all worked out quite nicely.
If you need to test any asp.net sites or controls, I highly recommend NUnitASP.
posted @ Thursday, March 18, 2004 12:30 AM