Tracking Selenium Commands Using C# and SQL Server 2008

When running a suite of Selenium tests, I wanted an easy way of looking at the pages visited as well as commands executed during each unit test. To solve this problem, I designed a solution built on top of the core Selenium classes that records all unit test data to a SQL Server 2008 database.

In this post, details of the following are covered:

  • Back End Design – Database schema overview.
  • Core logic – The class that drives tracking of the test.
  • Example Unit Test – Example of tracking a simple unit test.

Not all code required to communicate with the database is listed in this post. You can either download the source from the project on SourceForge by going to https://sourceforge.net/projects/seleniumtrack/ or browse the source in the project’s SVN repository.

The Back-End Design

The SQL Server database consists of 5 tables:

  • Test Suite – All test suites executed.
  • UnitTest – All unit tests for each test suite.
  • CommandLog – Commands executed for each unit test.
  • PageLog – Page or URL accessed by a command.
  • Cookies – Cookies values, if any, available when viewing a page.

tracking database schema

Tracking Database Schema


 

Core Logic

There’s one main class the directs the traffic, TrackDefaultSelenium. Inheriting from DefaultSelenium, the commands start, stop, open and click are recorded.

It’s here where test suites and unit test records are saved in the database. For each command targeted at the TrackDefaultSelenium class, records are saved which can be easily tied back to their respective unit tests.


    public class TrackDefaultSelenium : DefaultSelenium, ISelenium

    {

        private string _screenshotPath = @"c:\build\SeleniumTests\Screenshots\";

        private string _currentUrl;

        private int _testSuiteId;

        private int _unitTestId;

        private bool _captureScreenShots = false;

 

        private string _testSuiteName;

        private string _unitTestName;

        public TestStatus UnitTestStatus { get; set; }

 

        public enum TestStatus

        {

            Running,

            Completed,

            Failed

        }

Constructors – Kicks off the creation of a test suite.


        public TrackDefaultSelenium(ICommandProcessor processor, string testSuiteName, string unitTestName, bool CaptureScreenshots)

            : base(processor)

        {

            this._currentUrl = "";

            this._testSuiteName = testSuiteName;

            this._unitTestName = unitTestName;

            this._captureScreenShots = CaptureScreenshots;

            this._testSuiteId = Tracking.CreateTestSuite(this._unitSuiteName);

        }

 

        public TrackDefaultSelenium(string serverHost, int serverPort, string browserString, string browserURL)

            : base(serverHost, serverPort, browserString, browserURL)

        {

            this._currentUrl = "";

            this._testSuiteId = Tracking.CreateTestSuite(this._unitSuiteName);

        }

Command OverridesStart creates the unit test, whereas, click and open record page hits.


        void ISelenium.Start()

        {

            this.UnitTestStatus = TestStatus.Running;

            this.commandProcessor.Start();

            this._unitTestId = Tracking.CreateUnitTest(this._testSuiteId, this._unitTestName, GetLocation());           

        }

 

        void ISelenium.Click(string locator)

        {

            this.commandProcessor.DoCommand("click", new string[] { locator });

            int fileId = RecordPageHit("click", GetLocation());

 

            if (this._captureScreenShots)

                CaptureScreenShot(fileId);

        }

 

        void ISelenium.Open(string url)

        {

            this.commandProcessor.DoCommand("open", new string[] { url });

            RecordPageHit("open", GetLocation());

        }

 

        void ISelenium.Stop()

        {

            Tracking.UpdateUnitTestStatus(this._unitTestId, UnitTestStatus.ToString());

            this.commandProcessor.Stop();

        }

Tracking and Screenshot Capture Methods


        private void CaptureScreenShot(int fileId)

        {

            if (!System.IO.Directory.Exists(this._screenshotPath + this._testSuiteId))

                System.IO.Directory.CreateDirectory(this._screenshotPath + this._testSuiteId);

 

            if (!System.IO.Directory.Exists(this._screenshotPath + this._testSuiteId + @"\" + this._unitTestId))

                System.IO.Directory.CreateDirectory(this._screenshotPath + this._testSuiteId + @"\" + this._unitTestId);

 

            CaptureEntirePageScreenshot(this._screenshotPath + this._testSuiteId + @"\" + this._unitTestId + @"\" + fileId + @".jpg", "");

        }

 

        private int RecordPageHit(string command, string Url)

        {

            // record the command

            int CommandId = Tracking.CreateCommandLogEntry(this._unitTestId, command, "");

            int PageId = Tracking.CreatePageLogEntry(CommandId, Url);

 

            // attempt to obtain all cookie values without causing test to fail

            string ASPNetSessionId = "";

            try { ASPNetSessionId = GetCookieByName("ASP.NET_SessionId"); } catch { }

 

            // record all cookie values

            if (ASPNetSessionId != "")

                Tracking.CreateCookieEntry(PageId, "ASP.NET_SessionId", ASPNetSessionId);

 

            // save the URL

            this._currentUrl = Url;

 

            return CommandId;

        }

    }

}

Example Unit Test

In this example, we hit a w3school’s page, check the page title and record every command directed at the Selenium server.


using System;

using System.Text;

using System.Text.RegularExpressions;

using System.Threading;

using NUnit.Framework;

using Selenium;

using Web.Diagnostics.Selenium;

 

namespace Web.Diagnostics.UnitTests.TripPages

{

    [TestFixture]

    public class HeaderMastheadLinks : TestSuiteBase

    {

        private ISelenium selenium;

        private StringBuilder verificationErrors;

 

        [SetUp]

        public void SetupTest()

        {

            this.BaseUrl = "http://www.w3schools.com/";

 

            //target the Selenium RC Server

            selenium = new TrackDefaultSelenium(new HttpCommandProcessor("localhost", 4444,

                "*chrome", this.BaseUrl), "Example Test Suite", "Example Unit Test", true);

 

            // test with an anonymous (not logged in, not recognized) user

            selenium.DeleteAllVisibleCookies();

 

            selenium.Start();

            verificationErrors = new StringBuilder();

        }

 

        [TearDown]

        public void TeardownTest()

        {

            try

            {

                selenium.Stop();

            }

            catch (Exception)

            {

                // Ignore errors if unable to close the browser

            }

            Assert.AreEqual("", verificationErrors.ToString());

        }

 

        [Test]

        public void Header()

        {

            try

            {

                selenium.Open("http://www.w3schools.com/");

                selenium.Click("link=Learn HTML5");

                selenium.WaitForPageToLoad("30000");

                Assert.AreEqual("HTML5 Tutorial", selenium.GetTitle());

 

                (selenium as TrackDefaultSelenium).UnitTestStatus =

                    TrackDefaultSelenium.TestStatus.Completed;

            }

            catch (Exception e)

            {

                (selenium as TrackDefaultSelenium).UnitTestStatus =

                    TrackDefaultSelenium.TestStatus.Failed;

                selenium.Stop();

                throw (e);

            }

        }

    }

}

Notice how the script matches that of one generated by the Firefox plugin. The only difference here is how we’re instantiating the Selenium object. Instead of using the DefaultSelenium object, we’re using our custom Selenium tracking object TrackDefaultSelenium.

Looking at the data captured from the test above, we get the following:

UnitTestResults

Advertisements

About Dave
Certified Sitecore Developer.

4 Responses to Tracking Selenium Commands Using C# and SQL Server 2008

  1. Pingback: A Smattering of Selenium #27 « Official Selenium Blog

  2. Darren says:

    Thats pretty sweet!

    What about logging of error messages?

    • Dave says:

      Didn’t get that far. I literally built this out in a couple of hours. There is a lot of opportunity here to expand on this or use it to generate ideas of how to better track/record Selenium tests.

      For our workplace, we let NUnit run the tests and report the error back to CruiceControl.NET. CCNet will list the pass/fail results in the build log.

  3. Shanmugavel says:

    I am using selenium RC with Visual Studio 2010 IDE and created tests.
    Here i am facing a problem in identifying dynamic objects.
    Object id looks like “ctl00_MainEntry_AreaCreationControl_AreaGridView_ctl12_AreaNameFooterTextBox”
    Here ctl12 is the part which is changing based on which row…But i want to enter the value irrespective of row position..

    //*[@id=’ctl00_MainEntry_AreaCreationControl_AreaGridView_ctl12_AreaNameFooterTextBox’] works fine in Selenium IDE when I click FIND button in IDE.
    But below are not working in selenium IDE when I click FIND.
    (1)
    //input[Contains(@id,’ctl00_MainEntry_AreaCreationControl_AreaGridView_ctl’) and Contains(@id,’_AreaNameFooterTextBox’)]
    (2)
    //input[@id,RegExp:’ctl00_MainEntry_AreaCreationControl_AreaGridView_ctl??_AreaNameFooterTextBox’]
    (3)
    //input[@id,glob:’ctl00_MainEntry_AreaCreationControl_AreaGridView_ctl*_AreaNameFooterTextBox’]
    (4)
    //*[@id,glob:’ctl00_MainEntry_AreaCreationControl_AreaGridView_ctl*_AreaNameFooterTextBox’]

    Can anybody help me on this… How can I identify this object?

    Thx in advance….
    SCSVEL
    scsvel@gmail.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: