Welcome to blogs.conchango.com Sign in | Join | Help

Welcome to blogs.conchango.com

Richard Griffin's Blog

WATiN/R Testing Design Pattern

Do you want your tests fixtures to look like this ?

 

        [TestMethod]
        public void EnterInvalidUsernameAndPassword()
        {
            //Navigate to home page
           
IE instance = HomePageManager.NavigatetoHomePage();           

            //Check that there are no items in the basket
           
if (!BasketManager.ValidateBasketIsEmpty(instance))
            {
                //if not clear it
               
BasketManager.ClearBasketItems(instance);
           

            //Sign out of the session
           
SignInManager.SignOut(instance);          

            //Attempt a signin with invalid user name
           
SignInManager.SignIn(instance, "test&tester.com", "fake", true);           

            //make sure that the oops message is visible and the text is correct
           
Assert.IsTrue(instance.ContainsText("Oops! You made a mistake!"));

            Assert.IsTrue(instance.ContainsText("Please enter a valid email address."));
        }

 

In this post I will provide a outline for an approach you can use to refactor your tests and create a more maintainable and flexible design to use in your WATiR/N tests.

 

The WATiR WebRecorder++ and the WATiN WebRecorder++ are great tools that help create test scripts quickly. In the short term they provide the ability to create lightweight test scripts by both the developer and the tester.  The recorders output code so that the user becomes familiar with seeing the language and becomes used to certain constructs in that language. Once the test script has been recorded, verification code is written in the form of asserts to ensure that the required level of functionality is being delivered. Based on these factors, the creation of test fixtures using WATiR/N is a two-stage process consisting of recording and verifying activities.


With a complex or enterprise web site, the test fixture starts to get rather large, rather quickly, bringing with it management and maintainability issues. When creating tests using this particular approach there are some problems; a large amount of copying and pasting happens as test fixtures start to overlap; resulting in duplicate code for testing components that are reused across the site on different pages; and the navigational code for requesting pages in the site is consistently the same. One way around this is to provide a template that contains the default actions that your test fixture requires, record the test and crank out the verification code, copy this and add the code to the template and stash this away. A simple and effective way to improve productivity as the main parts of the test are predefined and heavily tested so if you test script barfs due to coding errors they are isolated to the new code that has been added to the template, allowing you to isolate the issue and quickly fix the problem.


The template helps but does not really solve the problems mentioned above. As a programmer we understand that low coupling and high levels of cohesion helps to make the code base easier to manage and maintain. Therefore, why not apply these principles to the test suite.  Therefore, I created a design that would increase cohesion and remove and need to copy and paste code, Don’t Repeat Yourself (DRY). To implement such a design, there needs to be a separation of concerns, by breaking away data, processing logic and test fixture. At this point it is important note that changes to HTML element id’s can cause your test fixture to break, and there is a question of responsibility over the tests. One of the reason that prompted the refactoring of the tests was based on observations that the developers were not owning the tests. So the tester spent more time maintaining existing tests rather than creating new tests scripts. For the moment it is best that we assume all parties are responsible.


Earlier I identified the three areas to be refactored, data, processing logic and the tests scripts that we want to rinse. Here I am going to classify the id’s and names of HTML elements  as the data part of the puzzle. Each page on the site needs to be mirrored to create a test data object, the data objects are simple property bags containing the id and names of the HTML elements located on the page. By creating this design, it tackles the issues around cohesion mentioned earlier. Having all the names and id’s of elements on the page in one place means that, when there is change on the page updating the variables happens in one place. The result, of the refactoring means that for each page on the web site there is a corresponding property bag class that contains the id’s of HTML elements and a property to access that HTML element.


The simple base structure, contains an abstract class  Abstract Page that implements an interface IPage. The interface provides the ability to access either a secure or an unsecure web page.  All other data classes that are created are derived from the AbstractPage class and therefore all data classes that are created retain the ability to have a secure and unsecure url’s. To adding a new interface or extending the existing IPage interface you will be able to add the extra functionality you require. The design is about containment and isolation, to allow for the auto generation of implemented pages and page components. The secure and unsecure Url properties are populated from a configuration file so that you can point the test scripts at a particular server with ease. A future enhancement that I would like to introduce is the ability for data classes to be auto generated using a CodeSmith template.


So now that we have meet the data aspect of the design, we can move on to the reusable logical processing actions of the  design. The functionality that is exposed or expected to be exposed is presented via a manager of the component or page. Therefore, in the current design there is a manager that you speak with to perform an action. The manager uses the data objects to access the HTML elements on the page to perform the task at hand requested. The processing logic is contained within the manager objects, it is here where you define common tasks on a page or component. Once there is duplication or common functionality requirements then this is the place where the code should live.


 

There is a one to one mapping between the managers and the page or control. Therefore there is a 1 to 1 mapping between the manager and the data class, you will notice that each implementation of the manager class are sealed so that they can’t be extended, they are closed. The next noticeable facet is that each of the managers are thread safe singletons. The managers are created from common code or duplicate code found in the outputted code of the recorder. Such action tasks as, “Login a user with the correct username and password” or a simple action, “navigate to homepage” make up the logical processing. These actions are common and are very reusable, achieving the goal of cohesion and enforcing the separation of concerns that we want from the pattern. They can also be made into your own toolset of controls that after a time provide a comprehensive framework. For instance most web sites that provide a mechanism to order products or service require a login and will have a shopping basket to order and pay for goods. By adding reusable basket testing components to your framework will help you become more productive.


        [TestMethod]
        public void EnterInvalidUsernameAndPassword()
        {
            //Navigate to home page
            IE instance = HomePageManager.NavigatetoHomePage();           

            //Check that there are no items in the basket
           
if (!BasketManager.ValidateBasketIsEmpty(instance))
            {
                //if not clear it
               
BasketManager.ClearBasketItems(instance);
           

            //Sign out of the session
           
SignInManager.SignOut(instance);           

            //Attempt a signin with invalid user name
           
SignInManager.SignIn(instance, "test&tester.com", "fake", true);           

            //make sure that the oops message is visible and the text is correct
           
Assert.IsTrue(instance.ContainsText("Oops! You made a mistake!"));
            Assert.IsTrue(instance.ContainsText("Please enter a valid email address."));
        }


The final piece of the puzzle are the test fixtures When using the recorder mechanism it can be easy to hit a curve where the QA person is spending more time doing maintenance tasks rather than creating test fixtures for the site. These test scripts that are produced can be confusing and prone to error caused by copy and pasting code from other areas of the test suite. To provide a solution to this the QA can now write there tests in a more English notation that can be easily read and understood, by a BA or developer. How is this achieved? This is done through the using interactions with the managers of the page and or the managers of component areas of the site to create a test script for a particular area of the system. Allowing the QA / tester  / developer to concentrate more on the creation of the test rather than the maintaining of the test. By designing layers of abstraction test fixtures can build high level readable tests.

 

The article has covered a mechanism for creating an abstract test design pattern based on using recorders that are commonly available to the development and testing teams. The ability of implementing an abstract design delivers a large amount of reusable code base that can be used on different projects that you work on, and could even be used as benchmarks for new projects starting up that have a similar level of functionality.

Published 14 November 2006 23:41 by Richard.Griffin
Filed under: , , , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

TrackBack said:

November 17, 2006 09:52
 

TrackBack said:

November 17, 2006 09:53
 

Eden Ridgway's Blog said:

WatiN - A .Net IE Testing Framework

January 11, 2007 04:19
 

stefano said:

Great article!

Is there any reason why manager classes cannot be static?

thanks

January 30, 2007 17:12
 

Ayende @ Rahien said:

Using WATIN for Integration Tests

February 15, 2007 20:21
 

stefano said:

yep, that's true.

February 21, 2007 13:25
 

Frederic Torres said:

Very interesting article.

We plan to introduce a similar concept in the record mode of our product InCisif.net in a next release.

Frederic Torres

www.InCisif.net

Web Testing with C# or VB.NET

February 25, 2007 23:01
 

Richard Beacroft said:

I have written some tests where I have used the decorator pattern.

The ConcreteDecorator representing a scenario to carry out and the ConcreteComponents representing web pages to populate.

The Component class is a basepage:

using System;

using System.Collections.Generic;

using System.Configuration;

using System.Data;

using System.Text;

using WatiN.Core;

using System.Collections.Specialized;

using System.Text.RegularExpressions;

namespace UI.Tests

{

public delegate void DataCaptured(NameValueCollection capturedItems);

internal abstract class BasePage

{

private string _protocol = "http://";

private string _server = (ConfigurationManager.AppSettings["Server"] == null) ? "localhost" : ConfigurationManager.AppSettings["Server"];

private bool _secure = (ConfigurationManager.AppSettings["Secure"] == null) ? false : bool.Parse(ConfigurationManager.AppSettings["Secure"]);

public BasePage()

{

Initialise();

}

public BasePage(ApplicationData applicationData)

{

Initialise(applicationData);

}

public event DataCaptured DataCapturedEvent;

protected abstract void Initialise(ApplicationData applicationData);

protected abstract void Initialise();

protected void FireDataCapturedEvent(NameValueCollection capturedItems)

{

if (DataCapturedEvent != null)

DataCapturedEvent(capturedItems);

}

public void GoTo(string url)

{

if (string.IsNullOrEmpty(url))

throw new ArgumentNullException("url");

Match match = (new Regex(@"(?<protocol>(https?|ftps?)\://)?(?<server>[a-zA-Z]+)?")).Match(url);

Group grpProtocol = match.Groups["protocol"];

Group grpServer = match.Groups["server"];

string protocol = (grpProtocol.Success) ? grpProtocol.Value : _protocol;

string server = (grpServer.Success) ? grpServer.Value : _server;

if (!url.StartsWith("/"))

url = "/" + url;

Browser.GoTo(protocol + server + url);

}

public bool Secure

{

get { return _secure; }

set

{

_secure = value;

if (_secure)

_protocol = "https://";

else

_protocol = "http://";

}

}

public string Server

{

get { return _server; }

set { _server = value; }

}

public IE Browser = null;

public abstract void Execute(NameValueCollection capturedItems);

}

}

Decorator Class:

using System;

using System.Collections.Generic;

using System.Text;

using WatiN.Core;

using System.Configuration;

using iE.NetFinance.Adapters;

using System.Collections.Specialized;

namespace UI.Tests

{

internal abstract class BaseScenario : BasePage

{

private NativeMethods.WindowShowStyle _windowStyle = NativeMethods.WindowShowStyle.ShowMaximized;

private NameValueCollection _captured = new NameValueCollection();

private List<BasePage> _pages = new List<BasePage>();

private bool _closeBrowser = true;

public BaseScenario() : base()

{

}

public BaseScenario(ApplicationData applicationData)

: base(applicationData)

{

}

/// <summary>

/// Windows style.

/// </summary>

public NativeMethods.WindowShowStyle WindowStyle

{

set { _windowStyle = value; }

get { return _windowStyle; }

}

public override void Execute(NameValueCollection capturedItems)

{

if (capturedItems != null && capturedItems.Count > 0)

_captured.Add(capturedItems);

foreach (BasePage page in _pages)

{

page.Execute(_captured);

}

if (_closeBrowser)

Close();

}

public void Close()

{

if (Browser != null)

{

Browser.Close();

Browser.Dispose();

Browser = null;

}

}

/// <summary>

/// Create browser

/// </summary>

/// <returns></returns>

public void CreateBrowserInstance()

{

IE browser = new IE();

browser.ShowWindow(_windowStyle);

Browser = browser;

}

protected bool CloseBrowserWhenFinished

{

get { return _closeBrowser; }

set { _closeBrowser = value; }

}

protected void Add(BasePage page)

{

page.Browser = Browser;

page.DataCapturedEvent += new DataCaptured(page_DataCapturedEvent);

_pages.Add(page);

}

private void page_DataCapturedEvent(NameValueCollection capturedItems)

{

foreach (string capturedItem in capturedItems)

{

if (_captured[capturedItem] == null)

_captured.Add(capturedItem, capturedItems[capturedItem]);

else

_captured[capturedItem] = capturedItems[capturedItem];

}

}

}

}

Please ignore the use of ApplicationData - this is specific to what we're doing...

I'd be interested in any comments.

Regards,

Rik

April 16, 2007 17:47
 

Michael Wittke said:

Hello,

WatIn is a great testing framework.

It was a hard work for me to find out how to find several elements of one type on a HTML-Site. Hence I thought it would be helpful for other beginners like him to post this here.

E.g. to find all checkboxes containing "checkbox_delivery_" on a website use the following snippet:

CheckBoxCollection checkboxes = this._ie.CheckBoxes.Filter(Find.ById(new Regex("checkbox_delivery_")));

Console.WriteLine(checkboxes.Length);

It is always the same procedure:

1) Make a collection of a special type

2) Use the filter function to find them

3) After that you can iterate through the result list.

Best regards

Michael

September 17, 2007 14:22
 

.Avery Blog said:

Watin Testing Pattern

October 10, 2007 01:13
 

BusinessRx Reading List said:

On my current project we have zero unit tests. I inherited the application and the existing unit tests

October 10, 2007 01:25
 

r_beacroft said:

Having played around with WatiN for a while now, I can safely say that the decorator pattern I mentioned here was only usefull from a devloper driven end-to-end test. As the focus should be on testers to define the tests and drive them I ended up changing the approach to take what now behaves something similar to wax.

October 25, 2007 13:52
 

thor said:

After installation of WatinRecorder I tried to run the shortcut and I got an error like this:

EventType: clr20r3  P1: watinrecorder.exe P2: 1.0.2482.35416

P3: 45368331  P4: watinrecorder  P5: 1.0.2482.35416  P6:  45368331

P7: 2f  P8: 1  P9:  system.io.filenotfoundexception

Any ideas on why i get this?  I have the free Visual C# 2008 and also DotNet frameworks 1.1 - 3.5, including 2.0SP1 .

March 18, 2008 17:22
 

Scott Hanselman's Computer Zen said:

July 1, 2008 01:28
 

*** enlargement said:

*** enlargement products that gives you fuller, firmer, larger, rounder perfect cup size *** in few weeks which increase the beauty of your body and gives you full confidence! www.big-***-enlargement.com

September 5, 2008 13:24
 

rüya tabirleri said:

Thank you very much :D

September 6, 2008 06:01
 

rüya tabiri said:

Thanks

September 6, 2008 06:01
 

parke said:

Thank you...

September 6, 2008 06:01
 

Sanjay said:

Came up with a problem .. i was trying to setup the environment and everything went fine except, after creating an instance of IE the object only visits desired webpage and does not execute rest of the codes at all. code given below

using System;

using WatiN.Core;

namespace ConsoleApplication1

{

   class Program

   {

       [STAThread]

       static void Main(string[] args)

       {

           IE ie = new IE();

           ie.GoTo("http://www.google.com");

           ie.TextField(Find.ByName("q")).TypeText("WatiN");

           ie.Button(Find.ByValue("Google Search")).Click();

           ie.Close();

         }

   }

}

any help would be appriciated .. thank you

October 21, 2008 21:30
 

brkin said:

Find more MEGAUPLOAD files free to download by visiting http://megauploadfiles.com/

January 2, 2009 21:17
 

Dixin said:

WatiN, stands for Web Application Testing In .NET, pronounced as what-in, is an easy to use, feature rich, and stable automated web application testing framework for the .NET languages. WatiN is developed in C# and aims to bring you an easy way to automate

February 4, 2009 16:49
 

granit said:

Thank you

March 14, 2009 18:19
 

Topics about Flowers » Wati Design is Now 20% off said:

April 5, 2009 05:34

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server (Personal Edition), by Telligent Systems