Tuesday, December 22, 2009

Testing code-behind with fitSharp

What do you do about domain logic in ASP.NET code-behind classes? Well, my first reaction to this question was the classic patient-doctor joke: "It hurts when I do this". "Then don't do that". But faced with this less-than-perfect situation in legacy code, is there any way to effectively test it? I wasn't sure, so I decided to try.

I wrote a little ASP.NET web forms application with two text boxes and a button that computes the sum of the values entered. I put my "domain" logic, the addition, in a code-behind class:

using System; namespace SampleWeb { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) {} protected void plusButton_Click(object sender, EventArgs e) { resultLabel.Text = (int.Parse(TextBox1.Text) + int.Parse(TextBox2.Text)).ToString(); } } }
To write a fitSharp test for this class, I'd like to write a fixture that creates  an instance of this class, sets the textbox contents, invokes the Click method and checks the result label contents.  But since these elements are all protected, it looks like I need to create an adapter that's a subclass of my code-behind class and that exposes the methods that I need for testing.  The other wrinkle is that the code-behind class in production is controlled by the ASP.NET framework, which takes care of creating the text box, button and label controls.  So I need to also take care of that in my adapter.  Here's what I came up with:

using System; using System.Web.UI.WebControls; namespace fitSharp.Samples { public class SampleWebAdapter: SampleWeb._Default { public SampleWebAdapter() { TextBox1 = new TextBox(); TextBox2 = new TextBox(); resultLabel = new Label(); } public string Text1 { set { TextBox1.Text = value; } } public string Text2 { set { TextBox2.Text = value; } } public string Result { get { return resultLabel.Text; } } public void Plus() { plusButton_Click(null, new EventArgs()); } } }
Now I can write a test page:
|sample web adapter|

And it works!  This does seem like rather a lot of test code, and it doesn't deal with issues like setting up HttpContext. But in this simple example, I was able to test domain logic in a code-behind class.

Adding Information to Fit Results

A frequently-asked question is how to add additional information to a Fit results page. There are several ways to do this, but here's a really quick and easy trick. The base Fixture class contains a table of summary information that any fixture can add to. The Summary fixture displays this information.

Here's a C# example that adds some additional information. Other languages will have different syntax, of course.

using fitlibrary; namespace fitSharp.Samples { public class SummarySample: DoFixture { public void AddLink() { TestStatus.Summary["mylink"] = "<a href=\"somewhere\">somewhere</a>"; } } }
Here's our FitNesse test page:

|add link|


Tuesday, December 15, 2009

System Metaphors and Deep Models

The book that revolutionized my approach to software development was a thin white volume titled eXtreme Programming explained. As I began to implement what I read, I struggled, like many others, to understand the System Metaphor practice. Even though Kent Beck explained that "sometimes the metaphor is 'naive'" [p.56], I felt like I was missing something. Why didn't my systems have catchy metaphors like C3's assembly line?

As my voyage continued, I discovered Eric Evans' Domain Driven Design and his Ubiquitous Language pattern. "Use the [domain] model as the backbone of a language ... in all communication within the team and in the code" [p.26]. I realized that Kent had also recommended that "the words used to identify technical entities should be consistently [chosen]" [p. 56]. So I stopped worrying about elegant metaphors and focused on expressing the domain language clearly in my code.

During the past year, my open source work on .NET functional test tools has evolved a new domain model, one that Eric describes as a Deep Model. "A deep model provides a lucid expression of the primary concerns ... while it sloughs off the superficial" [p.190]. As I developed more abstract and powerful concepts, I found I needed some way to hang these new ideas on a mental framework that would keep them consistent and understandable. I started drawing terms from a simple hardware model: processor, operator, memory. I was developing a System Metaphor!

Now I understand that a metaphor is not something that I need to seek from the outset. I concentrate on developing and refining a domain model and using the model language ubiquitously. At some point, in some domains, a deeper model may emerge with more insightful and powerful concepts. A metaphor is often a useful aid to organizing and explaining these concepts, by allowing us to relate abstract ideas to elements in a common concrete domain.

Tuesday, December 8, 2009

Adding External Pages to FitNesse

Caveat: This will be of interest only in the tiny world of FitNesse maintainers!

I've recently completed a version 1 implementation that allows folders of HTML files to appear as read-only pages in the FitNesse wiki. These pages can be executed as tests, navigated and searched as if they were actual wiki pages. This is a brief technical description of this implementation.

FitNesse already contains a class hierarchy of wiki page types, rooted at WikiPage. FileSystemPage is the concrete class that represents a normal wiki page. I added two new classes, ExternalSuitePage and ExternalTestPage, to represent a folder of HTML files and an individual HTML file.

FileSystemPage had an existing method, createChildPage, that created FileSystemPage objects for each of its sub-folders. I modified this method to use the following rules:
  • a folder with a context.txt file is a FileSystemPage
  • a folder with any *.html files is an ExternalSuitePage
Each WikiPage implementation has to provide methods for page content, attributes and children. The content for ExternalSuitePage is always "!contents" and its attributes are "Suite". It has ExternalTestPage children for all its HTML files and ExternalSuitePage children for all its subfolders of HTML files. The content for ExternalTestPage is its file contents wrapped in "!-" and "-!" and its attributes are "Test".

Within the scope of my changes, I made a couple of refactorings. I created a PageRepository class to encapsulate the knowledge of how pages are stored in the file system. I also created a FileSystem interface with production (disk-based) and test (memory-based) implementations so I could write tests that created various file system states without hitting the real file system.

This first version of the feature works well: I've used it on my fitSharp test suite of over a hundred HTML files. But there are a few caveats:
  • If you use the ?edit or ?properties URL syntax, you can get to the edit and properties pages, but they will fail if you try to save.
  • The contents of the HTML files are placed as is inside a FitNesse page, and may break the page display if there are unbalanced tags or other HTML problems that may not be apparent when the HTML is displayed on its own.
  • Hyperlinks, images, Javascript files and other external references in the HTML may not behave as expected when placed inside a FitNesse page.
  • Wiki page names are generated from the HTML file names, but there is no check to avoid potential name collisions. For example, TestA.html and testa.html will both generate TestA as a wiki page name.
Feel free to work on any of these issues - I'll probably come back to it in a while but I've got a list of other things I want to work on!