Getting Started With Selenium For ASP.NET Developers

During the Olympia Software Craftsmanship Workshop last weekend, Jeff Olsen (@olsonjeffery) demonstrated the use of the web testing framework Selenium. I was really impressed with the Selenium IDE and how easy it was to use in Firefox. One of the menu items of the IDE is to export a test as C#.

Export

This piqued my curiosity and I wanted to find out exactly what it takes for a .NET developer to get started using Selenium. This post is intended to help others get started.

First, a list of the stuff you will want to download:

If you want to integrate your tests into your build process or write tests as if they were unit tests, you will need the two asterisked items at a minimum. The Selenium Remote Control comes in two parts. There is a server that runs under the Java Runtime Environment and several Client libraries for just about any flavor of programming you may prefer.

SeleniumRC

As, you can see I have added a batch script to launch the server on my local machine. The file contains a single command:

"C:\Program Files (x86)\Java\jre6\bin\java.exe" -jar selenium-server-1.0\selenium-server.jar
If you run the batch script, this is what you will see.

SeleniumRunning

Selenium is now listening on its default port of 4444 for commands from the client library. The server portion of Selenium can run anywhere. It would be fairly easy to set it up to run on a VM or on your build server with minimal effort.

So now let's write some tests. This blog has a built in administrative system. After you authenticate, a link bar is displayed with the various administrative tasks. A simple set of specifications, might look like this:

  1. When authenticating with valid credentials, the configuration menu link should be present.
  2. When authenticating with invalid credentials, the configuration menu link should not be present.
    Let's start by creating a new test assembly. I called mine NotMyself.Blog.Specifications. Simply create a new Class Library and call it what ever you want. Next add references to nmock, nunit.core, nunit.framework and ThoughtWorks.Selenium.Core located in the selenium-dotnet-client-driver-1.0 folder in the Selenium Remote Control distribution.

References

I am not sure if there is a direct dependency on nmock or nunit for that mater. ThoughtWorks.Selenium.Core.dll is the meat & potatoes here. In a future post, I'll try getting up and running with MSTest or MBUnit.

To execute against the Selenium Server, we need to do some set up. The following base class demonstrates what is needed.

using System;
using System.Text;
using NUnit.Framework;
using Selenium;

namespace NotMyself.Blog.Specifications
{
  public class SeleniumTestContext
  {
    protected ISelenium browser;
    protected StringBuilder verificationErrors;

    [SetUp]
    public void SetupTest()
    {
      browser = new DefaultSelenium("localhost", 4444, "*iexplore", "http://iamnotmyself.com/");
      browser.Start();
      verificationErrors = new StringBuilder();

      Context();
      BecauseOf();
    }

    public virtual void Context() { }
    public virtual void BecauseOf() { }
    [TearDown]
    public void TeardownTest()
    {
      try
      {
        browser.Stop();
      }
      catch (Exception)
      {
        // Ignore errors if unable to close the browser
      }
      Assert.AreEqual(", verificationErrors.ToString());
    }
  }
}

The above class sets up a couple fields and then news up a DefaultSelenium object. This object takes four constructor parameters; the URL hosting the Selenium Server, the port the server is running on, a string that represents the browser we want the tests run in (in this case IE, but we could use "*chrome" to use FireFox) and finally the root URL of the site we are testing.

Next up let's create a context for our authentication tests.

namespace NotMyself.Blog.Specifications
{
  internal class AuthenticationContext : SeleniumTestContext
  {
    protected string authenticationUrl = "/notreallythe/pathtomy/page.aspx";
    protected string badPassword = "reallynotmypassword";
    protected string goodPassword = "notreallymypassword";
    protected string userName = "notreallymyusername";
  }
}

This class simply allow me to share some information across multiple test classes. Finally we are ready to write our tests.

using NUnit.Framework;

namespace NotMyself.Blog.Specifications
{
  [TestFixture]
  public class When_Authenticating_With_Valid_Credentials : AuthenticationContext
  {
    public override void BecauseOf()
    {
      browser.Open(authenticationUrl);
      browser.Type("LoginBox_username", userName);
      browser.Type("LoginBox_password", goodPassword);
      browser.Click("LoginBox_doSignIn");
      browser.WaitForPageToLoad("30000");
    }

    [Test]
    public void The_Configuration_Menu_Link_Should_Be_Present()
    {
      Assert.IsTrue(browser.IsElementPresent("_ctl6_hyperLinkEditConfig"));
    }
  }

  [TestFixture]
  public class When_Authenticating_With_Invalid_Credentials : AuthenticationContext
  {
    public override void BecauseOf()
    {
      browser.Open(authenticationUrl);
      browser.Type("LoginBox_username", userName);
      browser.Type("LoginBox_password", badPassword);
      browser.Click("LoginBox_doSignIn");
      browser.WaitForPageToLoad("30000");
    }

    [Test]
    public void The_Configuration_Menu_Link_Should_Not_Be_Present()
    {
      Assert.IsFalse(browser.IsElementPresent("_ctl6_hyperLinkEditConfig"));
    }
  }
}

These tests simply issue commands as if it were interacting directly with the browser telling it to open a URL, type some text into form fields, click a button. Validation works the same way, I interrogate the browser to see if specific elements exist.

Finally, all we have to do is fire up the Selenium Server by running the batch script, load up our test assembly into our favorite test runner and watch the tests execute. If you are running the Selenium Server locally you can sit back and watch the browser fire up and the tests execute right before your eyes. Very cool.

TestResults

Follow me on Mastodon!