Strategy for unit testing a web UI

As the loyal followers of my blog know, I'm a big test driven development (TDD) advocate.  I try and do all new development using TDD.  Sometimes I fall back into my old ways but a couple ugly classes, and a few bugs usually keep me in line.  The one area where I've been a little lax in my automated unit testing is in regards to the UI of my applications.  As expected I often see bugs cropping up within the UI of the applications I work on.  Since there aren't any unit tests to validate the UI I don't know about them until our QA analyst uncovers them, or even worse the customer.  I've recently been putting some thought into the best method for testing UI's, specifically of the web variety.  My current thoughts follow....

Often times when writing web based applications we wire up logic for persisting data entered into a web form within the Click event handler for the save button on the page.

protected void saveButton_Click(object sender, EventArgs e) {
   if(Page.IsValid) 
        // load values entered into web form into business object
        Customer c = new Customer();
        c.FirstName = firstName.Text;
        c.LastName = lastName.Text;
        ....

        new CustomerRepository.SaveCustomer(c);
   }
}

The problem with this approach is it's difficult to test the logic within the save button click event.  One of the methods I've thought about using to ease the testing of the logic involves creating an interface for the "view" that specifies all the data that is entered within the web form.

public interface ICustomerView {
   string FirstName { get; set; }
   string LastName { get; set; }
}

The second step, after the view is defined is to create a controller that loads, and reads the view.

public class CustomerController {
   public void LoadView(ICustomerView view, Customer customer) {
      view.FirstName = customer.FirstName;
      view.LastName = customer.LastName;
      // ... others
   }

   public void ReadView(ICustomerView view, Customer customer) {
     customer.FirstName = view.FirstName;
     customer.LastName = view.LastName;
      // ... others
   }
}

Now we have an interface as well as a controller that we can test using our usually unit testing methods.  Ideally we'd also write some tests to ensure that the web form that is implementing the ICustomerView interface properly loads and reads the controls that are on the form, however, with the current set of tools we have available I don't know of a way to handle that.  We can use NUnitAsp to test that the controls exist and that we can read the values, however, we can't do this through our controller class.  We also aren't able to ensure the base page class implements the necessary interface (ICustomerView).

Our web form utilizing the ICustomerView and Controller classes is below:

public

class CustomerEdit : System.Web.UI.Page, ICustomerView {
   protected TextBox firstName;
   protected TextBox lastName;
   protected Button saveButton;
   CustomerController controller = new CustomerController();
   CustomerRepository repository = new CustomerRepository();
   Customer customer = null;


   public int CustomerID {
      get { // read customer id from querystring, etc. }
   }

   #region

ICustomerView Members
   public string FirstName {
      get { return firstName.Text; }
      set { firstName.Text = value; }
   }

   public string LastName {
      get { return lastName.Text; }
      set { lastName.Text = value; }
   }
   #endregion

   private void Page_Load(object sender, System.EventArgs e) {
      if (!Page.IsPostBack) {
         customer = repository.GetCustomer(this.CustomerID);
         controller.LoadView(this, c);
      }
   }

   private void saveButton_Click(object sender, EventArgs e) {
      if (Page.IsValid) {
         customer = new Customer();
         controller.ReadView(this, customer);
         repository.SaveCustomer(customer);
      }
   }
}

The final step in the process is to verify that the view actually works.  To test this NUnitAsp can be used to set the values of the controls on the web form, and then "clicking" the save button.

[TestFixture]
public class CustomerViewTest : WebFormTestCase {
   public CustomerViewTest() {}

   TextBoxTester firstName;
   TextBoxTester lastName;
   ButtonTester saveButton;

   protected override void SetUp() {
      firstName = new TextBoxTester("firstName", CurrentWebForm);
      lastName = new TextBoxTester("lastName", CurrentWebForm);
      saveButton = new ButtonTester("saveButton", CurrentWebForm);
      Browser.GetPage(http://localhost/UITesting.Web/WebForm1.aspx);
   }

   [Test]
   public void ControlsAreVisible() {
      AssertVisibility(firstName, true);
      AssertVisibility(lastName, true);
      AssertVisibility(saveButton, true);
   }

   [Test]
   public void PressingSaveButtonWillSaveCustomer() {
      firstName.Text = "Steve";
      lastName.Text = "Eichert";
      saveButton.Click();
      
     
CustomerCollection customers = new CustomerRepository().FindAll();
      AssertEquals(1, customers.Count);
      Customer c = customers[0];
      AssertEquals(firstName.Text, c.FirstName);
      AssertEquals(lastName.Text, c.LastName);
   }
}

Note: I couldn't actually get my tests to run within NUnit so they might not work.  I keep getting a FileNotFoundException when trying which was a bit of a bummer.  Does anyone have NUnitAsp running on NUnit 2.2? 

What strategy do you use to run automated unit tests for your UI?

# re: Strategy for unit testing a web UI

Saturday, January 01, 2005 7:49 PM by Thomas Eyde    
How do you set up special state for the aspx under test? What if you need a 'fake' repository, because the 'real' one belongs to an environment where you can't modify it at will?

Finally, my biggest struggle and somewhat related to my first issue: How do you test a page you can't set up, but have to navigate to? This is why I gave up NUnitAsp, because I didn't want my tests to depend on navigation and hitting the right buttons on previous pages.

# re: Strategy for unit testing a web UI

Saturday, January 01, 2005 11:33 PM by Steve    
Thomas,
I was hoping you'd figure all that out :) It still isn't as easy as I'd like it to be to handle the things that you mention. I'm just starting to put some focus on figuring out how to do some of the things you mention so when, or shall I say if, I make progress I'll be sure to blog the details.

At this point NUnitAsp doesn't come close to giving me everything I want or need, but it does provide some level of testing which is better then none. As I mentioned in my post I couldn't actually get my tests to load in NUnit (v2.2) so I might not be using NUnitAsp afterall.

# re: Strategy for unit testing a web UI

Sunday, January 02, 2005 6:24 AM by Thom Lawrence    
Seen this?

<a target="_new" href="http://selenium.thoughtworks.com/">http://selenium.thoughtworks.com/</a>

# re: Strategy for unit testing a web UI

Sunday, January 02, 2005 9:49 AM by Steve    
I did see that a while ago but had forgotten about it. Have you used it? How did you make out with it?

Thanks for the reminder!

# re: Strategy for unit testing a web UI

Sunday, January 02, 2005 8:15 PM by Atilla Ozgur    
I would recommend watir with ruby. I would test this page setup with this script.

# test for button
require 'setup'

class TC_Button &lt; Test::Unit::TestCase
# Setup methond like Nunit
# all test* methods will execute it.
def setup
gotoButtonPage()
end
def gotoPage()
$ie.goto(&quot;button.html&quot;)
end

def test_textField
# does textboxes exist, visible in Asp.Net
assert($ie.textField(:id, &quot;firstName&quot;).exists?)
assert($ie.textField(:id, &quot;lastName&quot;).exists?)
#find a text input by id and set it.
$ie.textField(:id, &quot;firstName&quot;).set(&quot;Steve&quot;)
$ie.textField(:id, &quot;lastName&quot;).set(&quot;Eichert&quot;)
$ie.button(:id, &quot;saveButton&quot;).click

# there should be assert here. But since watir works with ie only, It can only find what is on browser. If your code does not show anything on UI, It can not test if your db calls or any other things is successfull
end

end

This test will initialize IE via OLE. Find the textboxes set values to them and click your button. Much better now. Ruby and watir is very easy to install and use. To work this test all you had to do.
cmd&gt; ruby buttonTest


# re: Strategy for unit testing a web UI

Tuesday, January 04, 2005 3:34 AM by Alex    
You can try SWExplorerAutomation (SWEA) SWExplorerAutomation works well with DHTML pages. It allows to record
script and generate in c# or VB.NET.

The tool is very powerful. You have full control over a Web Application
running inside Internet Explorer.

The idea is very simple. We define a scene for a set of common web pages.
Every scene has a descriptor. The descriptor unique identifies the set of
pages. It consists of title and url patterns. The patterns are regular
expressions, so one scene can match many pages. We access a scene content
by defining controls. SWEA implements the following controls: HtmlContent,
HtmlAnchor, HtmlInputText, HtmlSelect, HtmlRadioButton, HtmlCheckBox,
HtmlTextArea, HtmlInputButton, HrmlImage. Each control has a descriptor. The
descriptor binds the control to the DOM element. To activate a scene we
should navigate browser to the url. The SWEA engine (Browser Helper Object)
identifies a scene, binds controls to DOM elements and activates a scene.
After that the scene is accessible from the code.

HtmlContent is more complex that other controls. It allows not only to get a
content, but extract a data from it using XpathDataExtractor or
TableDataExtractor. Those objects have list of named expressions. The
expressions are defined visually at design time.

The main problem of Web automation is continues changes. The main idea of
SWEA is to separate the changes from a code. I am not able to say that I
completely solved the problem, but I minimized time needed to repair a
broken application.

I learned a lot developing SWJobSearch. SWJobSearch is a Smart Client
application which simultaneously searches for jobs on multiple Web Sites.
The Web sites were changed many times. I have started from 1 hour to repair
and finished by 5 minutes in current version, mostly because of SWDesigner.
I am really thinking about automatic repairs. So SWEA will cope with a Web
site changes automatically.

Alex.
alexfurman.net

# re: Strategy for unit testing a web UI

Sunday, January 13, 2008 10:06 PM by ftorres    
InCisif.net is a functional web testing tool for the .NET Platform (C#, VB.NET, IronPython).
* The Record mode is an interactive code generator (Web UI/DOM Viewer)
* Object Oriented API with tracing
* Integrated with Microsoft Visual Studio
* Advanced testing framework with parameterized test, test depencies, UI, but open to NUnit or other testing framework

Post a Comment

 
 
Prove you're not a spammer: 
4 + 4 =