A little while back I blogged about setting up and using WatiN and detailed how to create a simple unit test and also use the WatiN Test Recorder to create a simple test run. I also mentioned that I was working on a controller framework to aid in the development of automated web application tests and make it a bit easier to knock up tests for new web applications. This post is the first in a series ( I am not sure how long the series will be yet but I'll let you know when I know) of posts that will explain the framework together with reasons why I have chosen to do things in a particular way.
I originally started working on my controller framework a couple of months ago and I was about a week or two into development when I came across this WATiN/R Testing Design Pattern article, which discusses the importance of separating the logic for controlling the page from the tests themselves ensuring that you have maximum re-usability of your page controlling logic. This was also one of the driving forces behind my framework but rather than using static classes and I wanted a more fluent interface that allowed my domain specific controllers to easily flow from one controller to the next making my tests even easier to read, something like this:
[TestMethod]
public void ChangeEmployeeDetails()
{
Northwind
.GoToEmployees()
.ViewDetails("Davolio,Nancy")
.ChangeDetails(new EmployeeDetails()
{
FirstName = "Test",
LastName = "Employee",
ReportsTo = "Steven Buchanan"
});
}
The application I was working on was a very large web site that contained a lot of wizard logic and when we were adding new functionality to the wizards we seemed to be spending a great deal of time entering dummy data just to get to the point in the wizard that we really wanted to test. For example, a typical wizard would be (each step containing its own validation logic meaning that specific data must be entered):
- Enter Member Details i.e. first name last name, date of birth, contact phone number etc
- Enter Address Details
- Enter Marketing Information
- Enter Package Details
- Enter Payment Details
- Show Package Summary
- Finish the wizard
Frequently throughout development of the application we were required to make modifications to these wizards with new user requirements etc, so in order to modify something on step 5 Enter Payment Details, and for us to test the UI functionality it would be necessary enter all of the details for steps 1 to 4 time and time again which soon became very repetitive, tiresome and boring. Also, our application is multi-lingual so there was a requirement to be able to automate the site using different language and regional settings.
So with this in mind I set about creating a framework of controllers that allowed me to concentrate on adding the functionality for automating parts of the web site without repeating a bunch of unit test code. The base controllers are quite simple, here is a class diagram showing the relationships:
As can be seen in the image above, there are three page controllers:
- HomePageController<T> - this serves as the main entry point into the web site and should represent the first page that a user would be directed to when they arrive at the web site, there should only ever be one home page controller per web site. The HomePageController<T> is the only controller that maintains an instance of the WatiN.Core.IE object, any call made to the Browser property of the HomePageController<T> will either a) use an existing IE instance, b) search for an existing IE instance that matches a pre-defined base URL or c) create a new instance of IE and navigate to the pre-defined home page.
- PageController<T> - any other page within the web site should be represented by controller that inherits from the PageController<T> class unless the page contains a wizard in which case the WizardPageController<T> should be used. The PageController<T> base class provides some simple methods that enable easier use of a fluent interface
- WizardPageController<T> - any page that contains a wizard should inherit from this controller as this base class provides functionality that enables easy manipulation of the wizard on the page from within the unit tests.
As mentioned in my previous post setting up and using WatiN, I shall be using the Northwind ASP .NET Sample web site with Ajax to demonstrate how you would create a simple controller framework, the following image is a snap shot of the Northwind home page:
And this image shows some of the controllers I have created for the Northwind web site:
As you can see, the NorthwindController inherits from the HomePageController<T> class serving as the entry point into the web site, when the controller is defined a custom attribute is used to define what the base URL for the web site is and also what the name of the home page is:
[Url("http://localhost/Northwind", "Default.aspx")]
public class NorthwindController : HomePageController<NorthwindController> {}
The NorthwindController contains properties that are controllers for other parts of the web site such as the Employees page, Supplier page etc, I have also added a SampleWizard page to the site to demonstrate the use of a wizard page controller. Each property simply returns a new instance of the required controller type on each get request and looks something like this:
public EmployeeController EmployeeController
{
get { return new EmployeeController(this, this); }
}
The NorthwindController also exposes methods with the following name signature PageController<T> GoTo<PageName>(). Each of these methods implement the WatiN logic necessary to navigate to the relevant page within the web site and then returns the page controller that represents that page, this allows the fluent interface to be used from within the unit tests, a typical method might look like this:
public EmployeeController GoToEmployees()
{
Browser.Link(Find.ByText("Employees")).Click();
return EmployeeController;
}
So the GoTo method simply uses the page controllers Browser instance (WatiN) to find a hyper link with the text value of "Employees" (this could equally look for a hyperlink with an ID) and then clicks the link, after clicking the link the EmployeeController is returned allowing the test writer to immediately take control of the page that is being navigated to. If the link doesn't go to a page that can be controlled by the EmployeeController then the test will fail and the developer will know that something has been changed incorrectly.
The subsequent page controllers also expose GoTo methods when the page they represent contain links to other parts of the web site and they will generally also methods that expose the functionality available on that page. I think the easiest way to demonstrate this is show you an image of the Northwind Employees.aspx page:
Looking at this page you can see a couple of functions that would need to be exposed by the EmployeeController, these would include InsertNewEmployee, ViewEmployeeDetails and EditEmployeeDetails, in this example we shall focus on the ViewEmployeeDetails method. So in order to view the details of an employee we need to know what the name of the employee is, so this defines the parameter that will be required on our method and we must also find the "Details" link that is in the table row that contains the name of the employee. The resultant method call looks something like this:
public EmployeeDetailsController ViewEmployeeDetails(string employeeName)
{
((TableCell) Browser.Span(Find.ByText(employeeName)).Parent).ParentTableRow.Link(Find.ByText("Details")).Click();
return EmployeeDetailsController;
}
The method uses the page controller's Browser (via WatiN) to find a span via it's text value containing the name of the required employee. We then need to get the span's parent (a table cell) and obtain the table cell's parent row, using this row we then look for the hyperlink that contains the text "Details" and click it. After clicking the details link the page will be redirected to the Employee Details page and therefore returns the EmployeeDetailsController which then exposes the functionality available on this page. The Employee Details page looks like this:
Therefore, the EmployeeDetailsController exposes a method called ChangeDetails which takes a EmployeeDetails class as a parameter. This class simply serves the purpose of holding all of the information that could be changed for any given employee. The ChangeDetails method looks something like this:
public EmployeeController ChangeDetails(EmployeeDetails details)
{
Browser.TextField(ByPartialId("txtFirstname")).IfExists<TextField>(tf => tf.TypeText(details.FirstName));
Browser.TextField(ByPartialId("txtLastname")).IfExists<TextField>(tf => tf.TypeText(details.LastName));
Browser.SelectList(ByPartialId("ddReportsTo")).IfExists<SelectList>(tf => tf.Select(details.ReportsTo));
Browser.Button(ByPartialId("btEdit")).Click();
//...further fields removed for brevity
return GoToEmployees();
}
As can be seen, this method takes the EmployeeDetails class, using the page controller's Browser (via WatiN) finds the relevant controls on the page and uses the values contained within the EmployeeDetails class to enter the details. Finally, the save button is clicked and the user is returned to the Employees page. The IfExists<T> method is an extension method that I have added that enables testing whether a WatiN element exists prior to performing a relevant action, this is not part of the WatiN install, I shall cover some of the extension methods and helpers that I have needed in a later post.
Conclusion
In this post I have given you a simple introduction to the WatiN controller framework and how you would use it within your WatiN test development. I shall be creating some more posts about various things, including controlling wizards with the WizardPageController and WatiN Extension Methods and Helpers, I encountered during development of the controller framework so stay tuned.
The source code for this is not currently on CodePlex or SourceForge but if people are interested in learning more and seeing the source code just leave a comment and if there is enough interest I shall post the code to one of these sites or just throw up a zip file. Oh and by the way if anyone can think of a better name for this I would appreciate it, there is currently WatiN, Wax, WatiN Fixture, Watin Controller Framework doesn't quite fit and I think the acronym WCF has already been taken? :-).