Click here to Skip to main content
Click here to Skip to main content

Automation Utility Using Open-Source Tool: WatiN

By , 16 Sep 2009
Rate this:
Please Sign up or sign in to vote.

Introduction

In this article, I have explained how we can use WaTiN for test automation by writing a small wrapper utility over the core WaTiN functionalities. WaTiN stands for “Web Application Testing in .NET”, and is derived from WaTiR which is “Web Application Testing in Ruby”. The WaTiN libraries can be downloaded from WatiN Libraries.

One of the issues faced while writing an Automation Test Suite is locating the controls and then checking the functionality. It is accentuated by the fact that our test fails because the automated test is unable to find a particular control in the application, in-spite of the fact that the developer has added it. A good way to tackle this issue is by writing a small wrapper class over the WaTiN library and using this class in the automated tests.

Background

I have used WaTiN in conjunction with NUnit, and there’s already an article for this - NUNIT and WatiN.

Using the Code

The wrapper utility is mainly a set of four classes along with a small Strings project. One of the classes is ConfigurationReader; I am using it so that I can specify the website URL, database connection string, user name, password etc., in the app.config. This way, we can modify the app.config for use in any application and need not modify the source code if the same application is deployed at different web servers.

using System.Configuration;

namespace Automation.Common
{
    /// <summary>
    /// Reads the app.config for the application DB name and username etc
    /// </summary>
    public static class ConfigurationReader
    {
        #region Public Methods

        /// <summary>
        /// Reads the HomePage URL from app.config
        /// </summary>
        /// <returns>Home Page URL</returns>
        public static string getHomePageUrl()
        {
            return getConfigurationValue( "WebSiteUrl" );
        }

        /// <summary>
        /// Reads the Database Connection String from app.config
        /// </summary>
        /// <returns>Connection String</returns>
        public static string getConnectionString()
        {
            return getConfigurationValue( "ConnectionString" );
        }
        .....
        ...

Then, we have an Enumerators class. This class basically has a list of controls that have been used in the application and have to be automated or have some functionality associated with them.

namespace Automation.Common
{
    public class Enumerators
    {
        public enum ControlType
        {
            Span,
            Link,
            Table,
            TableCell,
            TableRow,
            Frame,
            Image,
            TextField,
            CheckBox,
            Button,
            SelectList,
            Div,
            RadioButton,
            FileUpload
        }
    }
}

Project Structure

Now we have the UseDialogOnce class; this is a third-party code I found on the internet. Basically, this class helps on handling pop-up dialogs and closing them as specified. Here is the link: UseDialogOnce.

Now, the core Utilities class. This class is the heart of the wrapper solution I am proposing. In this class, we have methods which act as wrapper methods over the core WaTiN methods, and help in locating the controls by ID, Custom etc., as WaTiN allows us to. The advantage of this class is that I check if the control exists before returning the object to the actual test we have written, thereby giving the exact message as to what was not found. Also, in this class, there are multiple overloads to locate the specified control inside a browser instance, or a dialog page, or a table, or an IFrame, and so on. There can be more overloads as required.

The first method of significance is NavigateToHomePage. This method can be called from the test classes we write, passing a reference to the existing browser instance. Other than that, it also checks if the browser instance exists or not; if not, it creates a new instance and navigates to the home page of the application. The home page URL can be taken from the app.config, as I mentioned earlier.

/// <summary>
/// Opens the Homepage of the web application
/// </summary>
/// <param name="ieBrowser">IE Browser Instance object</param>
public static void NavigateToHomePage( ref IE ieBrowser )
{
    if ( ieBrowser == null )
    {
        ieBrowser = new IE();
        ieBrowser.ShowWindow( NativeMethods.WindowShowStyle.Maximize );
    }
    ieBrowser.GoTo( ConfigurationReader.getHomePageUrl() );
    ieBrowser.WaitForComplete();
}

Another major method of this class is FindControlInBrowserById. Similar to this method, there are other methods like FindControlInBrowserByCustom, SelectControlInBrowserByID etc., I won’t be going into the details of each method. Below is the FindControlInBrowserById:

/// <summary>
/// Finds the control specified by id in the browser
/// </summary>
/// <param name="ie">IE Object</param>
/// <param name="strID">Id of the Control to be found</param>
/// <param name="ctrl">Type of Control</param>
/// <returns>The control if found, else "null"</returns>
public static object FindControlInBrowserByID( IE ie, string strID, 
                     Enumerators.ControlType ctrl )
{
    if ( ctrl == Enumerators.ControlType.Span )
    {
        Span sp = ie.Span( Find.ById( strID ) );
        Assert.IsTrue( sp.Exists, "Could not Find: " + strID );
        return sp;
    }
    else if ( ctrl == Enumerators.ControlType.Link )
    {
        Link lnk = ie.Link( Find.ById( strID ) );
        Assert.IsTrue( lnk.Exists, "Could not Find: " + strID );
        return lnk;
    }
    else if ( ctrl == Enumerators.ControlType.Frame )
    {
        Frame iFrame = ie.Frame( Find.ById( strID ) );
        return iFrame;
    }
    else if ( ctrl == Enumerators.ControlType.Image )
    {
        Image img = ie.Image( Find.ById( strID ) );
        Assert.IsTrue( img.Exists, "Could not Find: " + strID );
        return img;
    }
    else if ( ctrl == Enumerators.ControlType.TableCell )
    {
        TableCell tCell = ie.TableCell( Find.ById( strID ) );
        Assert.IsTrue( tCell.Exists, "Could not Find: " + strID );
        return tCell;
    }
    else if ( ctrl == Enumerators.ControlType.Table )
    {
        Table tbl = ie.Table( Find.ById( strID ) );
        Assert.IsTrue( tbl.Exists, "Could not Find: " + strID );
        return tbl;
    }
    else if ( ctrl == Enumerators.ControlType.TableRow )
    {
        TableRow row = ie.TableRow( Find.ById( strID ) );
        Assert.IsTrue( row.Exists, "Could not Find: " + strID );
        return row;
    }
    else if ( ctrl == Enumerators.ControlType.CheckBox )
    {
        CheckBox chk = ie.CheckBox( Find.ById( strID ) );
        Assert.IsTrue( chk.Exists, "Could not Find: " + strID );
        return chk;
    }
    else if ( ctrl == Enumerators.ControlType.Button )
    {
        Button btn = ie.Button( Find.ById( strID ) );
        Assert.IsTrue( btn.Exists, "Could not Find: " + strID );
        return btn;
    }
    else if ( ctrl == Enumerators.ControlType.TextField )
    {
        TextField txt = ie.TextField( Find.ById( strID ) );
        Assert.IsTrue( txt.Exists, "Could not Find: " + strID );
        return txt;
    }
    ......
    ....
    ..
    ...
    else
    {
        return null;
    }
}

On the same lines, there are methods like ClickLink, SelectControlInBrowserById, AddTextInTextBox etc. The ClickLink method clicks a link in the browse, or table, or dialog page as specified. SelectControlInBrowserById is useful for controls which have an action associated with them and we need to perform that action to proceed to the next step in the test. AddTextInTextBox, as the name suggests, adds the specified text in the textbox control specified by the ID.

Finally, how to use this wrapper we have written. It's pretty easy to do that; below is a sample code of a simple create record test:

namespace Automation.Tests
{
    [TestFixture]
    public class JailRecordTest
    {
        IE ie;

        [TestFixtureSetUpAttribute]
        public void SetUp()
        {
            StartProcess();
        }

        [Test]
        [Description( "Verify the Title of the HomePage" )]
        public void Verify_HomePage_PageTitle()
        {
            Assert.AreEqual( AppStrings.HomePageTitle, ie.Title );
        }

        [Test]
        [Description( "Create a New Instance of Jail Record" )]
        public void Create_New_JailRecordTest()
        {
            Frame appFrame =
                ( Frame )Utilities.FindControlInBrowserByCustom( ie, "name", 
                  AppStrings.AppFrame, Enumerators.ControlType.Frame );

            Table menuBarTable =
                ( Table )Utilities.FindControlInFrameByID( appFrame, 
                  AppStrings.AppMenuBar, Enumerators.ControlType.Table );

            Image newButtonImage =
                ( Image )Utilities.FindControlInTableByID( menuBarTable, 
                  JailRecordStrings.NewJailRecordImage, 
                  Enumerators.ControlType.Image );
            newButtonImage.Click();

            //Creating the Record once the new IE instance opens

            ie = IE.AttachToIE( Find.ByTitle( JailRecordStrings.NewJailRecordPageTitle ) );

            Utilities.AddTextInTextBox( ref ie, 
              JailRecordStrings.NewJailRecordJailIdTxtBox, "Demo Record" );

            Table jailRecordMenuBar =
                ( Table )Utilities.FindControlInBrowserByID( ie, 
                  JailRecordStrings.NewJailRecordMenuBar, Enumerators.ControlType.Table );

            Image saveAndCloseButtonImage =
                ( Image )Utilities.FindControlInTableByID( jailRecordMenuBar, 
                  JailRecordStrings.NewJailRecordSaveCloseImage,
                  Enumerators.ControlType.Image );
            saveAndCloseButtonImage.Click();

            ie = IE.AttachToIE( Find.ByTitle( AppStrings.HomePageTitle ) );
        }
        
        #region Private Methods

        private void StartProcess()
        {
            Utilities.NavigateToHomePage( ref ie );
        }

        #endregion

        [TestFixtureTearDownAttribute]
        public void TearDown()
        {
            ie.Close();
        }
    }
}

This test was written for automating a CRM like web application, so we had to locate the frame first, then the control, primarily to improve the performance of the test. So first, we find the frame, then the control inside it, and finally, we have a MenuBar which has a “New” button. We find the button and click it. After clicking it, a new browser window is opened, hence the IE.AttachToIE method is used. In the new browser window, we enter the required data and then find the “Save” button as we had found the “New” button, and save the record. The verification part, if the record has been created, can be done quite easily, by looking up for the record name in the grid shown on the main page (CRM specific – that’s why I have removed that portion from the code). Now, if you analyze the code, if we had used only WaTiN instead of the wrapper utility, we would have ended up with approximately 100 lines of code in the Test method, and it would have been very confusing as to where we are locating the actual controls and then checking their functionalities. With this wrapper, the Test method is reduced to less than half of that, and the best part is that, the utility can be used across the entire web application.

I hope this Wrapper utility helps my fellow Test Automation developers, and is useful in automating complex web applications. Any suggestions or improvements, I can be contacted anytime. Thank you.

Points of Interest

As I did mention earlier, finding a web control in a web application is one of the toughest parts for a Test Automation Developer. The only way to ensure that your automated test runs alright is to make sure that the developers always provide an ID for the web control they are using in the web application. I had a really tough time convincing developers to make it a practice.

Also, we were using Telerik and Rad Controls in our applications extensively. Automating them was a very big issue, as it's difficult to find the way they are rendered (even with the IE Developer Tool). I could discuss that in another article, if required.

History

The WaTiN DLL I have used in this project is 1.2.1.4000; as of now, the latest release is 2.0 Beta 1. I will be upgrading the project to use that soon.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Diwakar Gupta
Meridium Services & Labs Pvt. Ltd.
India India
Working as a Technical Consultant in the EAM domain using ETL technologies. APM & Reliability tools installation, technical troubleshooting & training.
 
Worked as an Associate Manager - Quality in eMids Technologies Pvt. Ltd. August 2010 - Nov 2011. Taking care of Delivery & Quality Assurance Reviews. Quality compliance and development of internal business intelligence portal - ARC.
 
Worked as a .NET Software Developer & Analyst in Proteans Software Solutions from Jan 2007 - August 2010. Worked extensively on Test Automation and Testing using open-source tools before shifting to .NET development tasks.
Primarily working on .NET & CRM platforms. Thorough knowledge of ORM-based architecture & MVC. Alongwith this have good SDLC knowledge and project management skills.
Follow on   Twitter

Comments and Discussions

 
Questionerror when running JailRecordTest [modified] Pinmemberclosl12-Dec-12 3:31 
GeneralMy vote of 5 PinmvpKanasz Robert28-Sep-12 5:52 
GeneralRe: My vote of 5 PinmemberDiwakar Gupta28-Sep-12 6:34 
GeneralMy vote of 5 Pinmemberjawed.ace18-Oct-11 22:48 
GeneralRe: My vote of 5 PinmemberDiwakar Gupta13-Nov-11 22:29 
GeneralMy vote of 5 PinmemberAbhinav Tickoo29-Jun-10 2:14 
GeneralThank You PinmemberAbinash Bishoyi12-Jan-10 21:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140421.2 | Last Updated 16 Sep 2009
Article Copyright 2009 by Diwakar Gupta
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid