Tracking Selenium Commands Using C# and SQL Server 2008
September 13, 2010 4 Comments
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.
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 Overrides – Start 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:














