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|

|set|text1|1|
|set|text2|2|
|plus|
|check|result|3|
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.

1 comment:

  1. Hey, let's assume I've had a serious brain injury (thus I am void of knowledge I should otherwise have).

    If, in my case, the methods in my codebehind are all private, rather than protected, does this complicate matters? This for me, includes Page_Load and button_Click-type methods.

    Shall I be going to devs with beer tickets and asking for refactoring?

    ReplyDelete