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

Saturday, January 7, 2012

.NET Collection Runtimes

Each week my department holds a morning tech review. Subjects vary from testing tools, advanced caching mechanisms, to good ol' basic .NET facts. This week the tech review was an "Asymptomatic Analysis of .NET Collections". While most of the material was pretty straight forward, I always find getting back to the basics to be a good thing.

I thought that it was fun to see all of the collection run times aggregated into one table. As several of my coworkers pointed out, it can be a bit of a pain to find all these "simple" facts on the SEO crammed internet...so then why does no one simply blog about it?

In summary: here is some stolen content. Thanks Byron!

List-Like Collections

  List LinkedList SortedList
Add O(1) or O(N) O(1) O(Log N) or O(N)
Remove O(N) O(N) O(N)
Clear O(N) O(1) O(Log N)
Get O(1) O(N) O(Log N)
Find O(N) O(N) O(Log N)
Sort O(N log N) or O(N^2) - Already sorted
Underlying
Data
Structure
Array Dynamically allocated nodes Array

Hash-Like Collections

  Dictionary SortedDictionary HashSet SortedSet
Add O(1)
or O(N)
O(Log N) O(1)
or O(N)
O(log N)
Remove O(1) O(log N) O(1) O(N)
Clear O(N) O(Log N) O(N) O(N)
Get / Find O(1) O(Log N) O(1) O(Log N)
Sort - Already sorted - -
Underlying
Data
Structure
Array + Linked List Dynamically allocated nodes
(Binary Search Tree)
Array + Linked List Dynamically allocated nodes
(Binary Search Tree)

If you would like to see any updates/additions please feel free to leave a comment.

Enjoy,
Tom

Tuesday, January 3, 2012

Why Atwood's Law is a Good Thing

Any application that can be written in JavaScript, will eventually be written in JavaScript.

Jeff Atwood proposed this simple rule way back in 2007. Now, as his predictions come true, we may find ourselves asking "why would anyone want to do such a crazy thing?" Well here are three simple reasons:

1: Because we can!
Software development is an art that requires practice. Why do college students write compilers? Why did John Carmack port Rage to iOS? Why did I port Chrono Trigger to a graphing calculator? Software engineers are artisans, these challenges represent our art form at it's best.

2: JavaScript represents true platform independence.
Windows, Mac, Linux, x86, x64, iOS, Android, the list goes on; frankly, there are just too many platforms out there to develop for. The beauty of JavaScript is that it is a universally recognized spec that is supported everywhere. This level of accessibility is advantageous for developers and consumers alike.

3: It shows the power of modern technology.
The fact that we can run a Java Virtual Machine in the browser shows just how far modern browsers and house hold hardware have come. It was only a few years ago that Virtual Machines were a concept reserved for only the most powerful of server farms. The tower of Dubia was not built out of necessity, it instead stands as a monument to human achievement; that is how I view emulator hardware in a browser, a virtual monument of human engineering.

...also, why wouldn't you want to play NES games in your browser?

Tom

Sunday, December 25, 2011

Happy Holidays

My apologies for the shortage of blog posts recently, but between the new job and some unannounced side projects I've been extremely busy. So here is some best of 2011 filler material...

  • Gizmo of the Year: Asus Transformer Prime
  • Console Game of the Year: Super Mario 3D Land
  • PC Game of the Year: Orcs Must Die
  • Movie of the Year: Troll Hunter
  • Programmer of the Year: Markus "Notch" Persson
  • Gift of the Year: FLYING SHARKS!

Happy Holidays,
Tom

Sunday, November 6, 2011

jQuery UI Modal Dialog disables scrolling in Chrome

Is Chrome becoming the new IE?

As much as I love jQuery, I still cannot escape the fact that jQuery UI leaves a lot to be desired. Yesterday I ran across an issue where the jQuery UI modal dialog acted inconsistently in different browsers. Normally opening a modal leaves the background page functionality unaltered, but in Webkit browsers (I ran into this while using Chrome) it disables the page scroll bars.

The Fix

Yes, this bug has already been reported. Yes, it is priority major. No, it won't be fixed anytime soon. For a feature as widely used as the Modal Dialog, I find that kinda sad.

However, thanks to Jesse Beach, there is a tiny little patch to fix this! Here is a slightly updated version of the fix:

(function($) {
  if ($.ui && $.ui.dialog && $.browser.webkit) {
    $.ui.dialog.overlay.events = $.map(['focus', 'keydown', 'keypress'], function(event) { 
      return event + '.dialog-overlay';
    }).join(' ');
  }
}(jQuery));

Additional Resources

Hope that helps!
Tom

Thursday, November 3, 2011

Configuring MVC Routes in Web.config

ASP.NET MVC is even more configurable than you think!

Routes are registered in the Application Start of an MVC application, but there is no reason that they have to be hard coded in the Global.asax. By simply reading routes out of the Web.config you provide a way to control routing without having to redeploy code, allowing you to enable or disable website functionality on the fly.

I can't take credit for this idea, my implementation is an enhancement of Fredrik Normén's MvcRouteHandler that adds a few things that were missing:

  • Optional Parameters
  • Typed Constraints
  • Data Tokens
  • An MVC3 Library

Download MvcRouteConfig.zip for the project and a sample application.

Example Global.asax

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
    var routeConfigManager = new RouteManager();
    routeConfigManager.RegisterRoutes(routes);
}

Example Web.config

<configuration>
  <configSections>
    <section name="routeTable" type="MvcRouteConfig.RouteSection" />
  </configSections>
  <routeTable>
    <routes>
      <add name="Default" url="{controller}/{action}/{id}">
        <defaults controller="Home" action="Index" id="Optional" />
        <constraints>
          <add name="custom"
              type="MvcApplication.CustomConstraint, MvcApplication">
            <params value="Hello world!" />
          </add>
        </constraints>
      </add>
    </routes>
  </routeTable>

Thanks Fredrik!

~Tom

Update 2/16/2013 - I have added this source to GitHub and created a NuGet Package.

Shout it

Monday, October 17, 2011

Using the InternetExplorerDriver for WebDriver

Are you getting this error when trying to use the InternetExplorerDriver for WebDriver (Selenium 2.0)?

System.InvalidOperationException

"Unexpected error launching Internet Explorer. Protected Mode must be set to the same value (enabled or disabled) for all zones. (NoSuchDriver)"

Don't worry, it's easier to fix than it sounds: Simply go into your Internet Options of Internet Explorer, select the Security tab, and all four zones to have the same Protected Mode value (either all on or all off). That's it!

Sample Code

using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
 
namespace WebDriverTests
{
    public abstract class WebDriverTestBase
    {
        public IWebDriver WebDriver { get; set; }
 
        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {
            WebDriver = new InternetExplorerDriver();
        }
 
        [TestFixtureTearDown]
        public void TestFixtureTearDown()
        {
            if (WebDriver != null)
            {
                WebDriver.Close();
                WebDriver.Dispose();
            }
        }
 
        [SetUp]
        public void SetUp()
        {
            WebDriver.Url = "about:blank";
        }
    }
 
    [TestFixture]
    public class GoogleTests : WebDriverTestBase
    {
        [Test]
        public void SearchForTom()
        {
            WebDriver.Url = "http://www.google.com/";
 
            IWebElement searchBox = WebDriver
                .FindElement(By.Id("lst-ib"));
 
            searchBox.SendKeys("Tom DuPont");
            searchBox.Submit();
 
            IWebElement firstResult = WebDriver
                .FindElement(By.CssSelector("#search cite"));
 
            Assert.AreEqual("www.tomdupont.net/", firstResult.Text);
        }
    }
}
Shout it

Enjoy,
Tom

Real Time Web Analytics