Monday, February 20, 2012

Browser Specific TestFixtures Sharing Tests

I will get to WebDrivers running in parallel next week, for now something even more fun: How to share Tests across browser specific TestFixtures with WebDriver.

When writing a unit test to test a webpage, you will want to duplicate that test against multiple browsers. How do we do accomplish this feat? Inheritance!

Step 1: SetUpFixture

First we need to create a SetUpFixture that will manage our WebDrivers. This will be largest and most complex class of our demonstration.

[SetUpFixture]
public class SetUpFixture
{
    private static IWebDriver _internetExplorerDriver;
    private static IWebDriver _chromeDriver;
 
    public static IWebDriver InternetExplorerDriver
    {
        get
        {
            if (_internetExplorerDriver == null)
            {
                DisposeDrivers();
                _internetExplorerDriver = new InternetExplorerDriver();
            }
            return _internetExplorerDriver;
        }
    }
 
    public static IWebDriver ChromeDriver
    {
        get
        {
            if (_chromeDriver == null)
            {
                DisposeDrivers();
                var dir = ConfigurationManager.AppSettings["chrome"];
                _chromeDriver = new ChromeDriver(dir);
            }
            return _chromeDriver;
        }
    }
 
    private static void DisposeDrivers()
    {
        if (_internetExplorerDriver != null)
        {
            _internetExplorerDriver.Close();
            _internetExplorerDriver.Dispose();
            _internetExplorerDriver = null;
        }
        if (_chromeDriver != null)
        {
            _chromeDriver.Close();
            _chromeDriver.Dispose();
            _chromeDriver= null;
        }
    }
 
    [TearDown]
    public void TearDown()
    {
        DisposeDrivers();
    }
}

Step 2: Base Class

Second we create an abstract base class that will go select our WebDriver from the SetUpFixture via an abstract property.

[TestFixture]
public abstract class TestFixtureBase
{
    public abstract IWebDriver WebDriver { get; }
 
    [SetUp]
    public void SetUp()
    {
        WebDriver.Url = "about:blank";
    }
}

Step 3: Tests

Write your tests in abstract classes that inherit from the base class.

public abstract class TestFixtureA : TestFixtureBase
{
    [Test]
    public void Test1()
    {
        WebDriver.Url = "http://www.phandroid.com/";
        Assert.IsTrue(WebDriver.Title.StartsWith("Android Phone"));
    }
 
    [Test]
    public void Test2()
    {
        WebDriver.Url = "http://www.reddit.com/";
        Assert.IsTrue(WebDriver.Title.StartsWith("reddit"));
    }
}

Step 4: Driver Specific TestFixtures

Create a test fixture for each permutation of browser and test class.

public class ChromeTestFixtureA : TestFixtureA
{
    public override IWebDriver WebDriver
    {
        get { return SetUpFixture.ChromeDriver; }
    }
}
 
public class InternetExplorerTestFixtureA : TestFixtureA
{
    public override IWebDriver WebDriver
    {
        get { return SetUpFixture.InternetExplorerDriver; }
    }
}

Step 5: Run!

Okay, next week I'll actually talk about running WebDrivers in parallel. :)

kick it on DotNetKicks.com

Enjoy,
Tom

Saturday, February 11, 2012

Sharing a WebDriver across TestFixtures

I absolutely love WebDriver.

WebDriver (also known as Selenium 2.0) is a web testing tool that is both useful and easy, which is a very rare find. If you are doing web development with ASP.NET, you need to take 30 minutes of your time and go try out WebDriver. That is all the time it will take to get you hooked.

WebDriver and NUnit

To launch a browser you need only new up a Driver object for that browser. I used to create a new Driver in my TestFixtureSetup, and then close and dispose of that in the testFixtureTearDown. However now that Firefox does not persist my windows login credentials it can be very frustrating to have to log back in for every test fixture.

A solution to this problem is simply to share a single WebDriver across multiple TestFixtures. Fortunately NUnit's SetUpFixture makes this very easy to do.

Step 1: Create a SetUpFixture

This class's SetUp method will be called once before any TestFixtures run, and then it's TearDown will be called once after all the TestFixtures have completed.

[SetUpFixture]
public class SetUpFixture
{
    public const string ProfileKey = "firefoxprofile";
 
    public static IWebDriver WebDriver { get; private set; }
 
    [SetUp]
    public void SetUp()
    {
        var profileDir = ConfigurationManager.AppSettings[ProfileKey];
        if (String.IsNullOrWhiteSpace(profileDir))
        {
            WebDriver = new FirefoxDriver();
        }
        else
        {
            var profile = new FirefoxProfile(profileDir);
            WebDriver = new FirefoxDriver(profile);
        }
    }
 
    [TearDown]
    public void TearDown()
    {
        if (WebDriver == null)
            return;
 
        WebDriver.Close();
        WebDriver.Dispose();
    }
}

Step 2: Create an abstract base class

This abstract class will use the TestFixtureSetUp to copy the static WebDriver that was initialized by the SetUpFixture. I also like to have a SetUp that will clear the browser, just to make sure you are navigation to a new page from a blank one.

public abstract class TestFixtureBase
{
    public IWebDriver WebDriver { get; private set; }
 
    [TestFixtureSetUp]
    public void TestFixtureSetUp()
    {
        WebDriver = SetUpFixture.WebDriver;
    }
 
    [SetUp]
    public void SetUp()
    {
        WebDriver.Url = "about:blank";
    }
}

Step 3: Make your TestFixtures inherit the base class

These TestFixtures will now share the static WebDriver.

[TestFixture]
public class TestFixtureA : TestFixtureBase
{
    [Test]
    public void Test1()
    {
        WebDriver.Url = "http://www.phandroid.com/";
        Assert.IsTrue(WebDriver.Title.StartsWith("Android Phone"));
    }
}
 
[TestFixture]
public class TestFixtureB : TestFixtureBase
{
    [Test]
    public void Test1()
    {
        WebDriver.Url = "http://www.reddit.com/";
        Assert.IsTrue(WebDriver.Title.StartsWith("reddit"));
    }
}

Step 4: Run those tests!

If you love .NET 4.0 then stay tuned, because in my next post I'll explore running WebDrivers in PARALLEL! See you next week. :)

kick it on DotNetKicks.com

Enjoy,
Tom

Real Time Web Analytics