Tuesday, December 16, 2008

Automated Testing w/ Selenium!

We here at CodeSmith recently started using Selenium to test our webapps.

If you haven't heard of Selenium, or you have been thinking about trying to create tests for any of your websites or web apps but didn't know where to start, go check it out! (Some links can be found at the bottom of the post.)

While Selenium is a great tool, and the guys at ThoughtWorks did a great job, Selenium definitely lacking in the same department as most all of it's open source brethren: documentation. Thus, this is my attempt to try and help out!

Our Automation Goals

Once we got our unit tests working, we wanted a quick and easy way to automate them; the goal was to be able to open N-Unit, run tests, and not have to worry about configuration and server connections. Also, this worked out well for running our unit tests with our build for continuous integration!

Disclaimer: For large scale or enterprise testing, there are better ways of accomplishing this (such as leaving the server running on a dedicated testing box), but we were just looking for a quick and easy way to run our tests!

Our Automation Implementation 

We are just running the Selenium Server out of the Test Fixture Setup; we launch it as a process, run our tests, then close it in the Test Fixture Tear Down.

First, we created a class to pull my settings from the App.Config...

public static class SeleniumAppSettings
{
    public static string ServerHost
    {
        get { return ConfigurationManager.AppSettings["SeleniumServerHost"] ?? "localhost"; } 
    }

...etc, etc, I only mention this so you know where I'm getting the values later!

Second, we create an abstract class for our Tests to inherit from...

public abstract class SeleniumTestBase
{
    private Process _process;
    protected ISelenium selenium;

    [TestFixtureSetUp]
    public void TestFixtureSetUp()
    {
        string arguments = String.Format("-jar \"{0}\" -firefoxProfileTemplate \"{1}\"",
            SeleniumAppSettings.ServerJar,
            SeleniumAppSettings.FirefoxProfile);
        _process = Process.Start("java", arguments);

        selenium = new DefaultSelenium(
            SeleniumAppSettings.ServerHost,
            SeleniumAppSettings.ServerPort,
            SeleniumAppSettings.Browser,
            SeleniumAppSettings.URL);
        selenium.Start();
    }

    [TestFixtureTearDown]
    public void TestFixtureTearDown()
    {
        if (selenium != null)
        {
            try
            {
                // Close Browser
                selenium.Stop();
                // Close Server
                selenium.ShutDownSeleniumServer();
            }
            catch { }
        }

        // Confirm server is closed.
        try
        {
            if (!_process.HasExited)
                _process.CloseMainWindow();
            if (!_process.HasExited)
                _process.Kill();
            _process.Close();
        }
        catch { }
    }
}

Third, we just created a class for whatever we wanted to test...

[TestFixture]
public class WebsiteTest : SeleniumTestBase
{
    [Test]
    public void MainPageTest()
    {
        selenium.Open("/");
        selenium.WaitForPageToLoad(SeleniumAppSettings.TimeOut);
        Assert.IsTrue(selenium.GetTitle().Equals("My Website"));
        Assert.IsTrue(selenium.IsTextPresent("Hello world!"));
    }
}

As long as it inherits the base it will take care of managing the server!

Firefox 3.0

A great feature with Selenium is that you can run your tests against multiple browsers just by customing your start up parameters. (...yes, my example is hard coded to use Firefox, see my next section for why!) So, many are disappointed that Selenium does not natively support Firefox 3.0...

However! There is a very simple, and very reliable work around! Here's what you need to do...

  1. Open selenium-server.jar (You can do this by using Winrar.)
  2. Recursively traverse the customProfileDirCUSTFFCHROME and customProfileDirCUSTFF directorys...
  3. ... and edit all the install.rdf files...
  4. ...update the <em:maxVersion> values from 2.0.0.* to 4.0.0.*

...this will allow Selenium to run the latest Firefox!

(My thanks to Mikhail Koryak for blogging about this fix!)

Persisting Custom SSH Certificates

When testing one of our websites we kept running into a problem: our test domains did not have legitimate SSH certificates, so we were unable to test our https pages!

Why/how was this a problem? When Selenium launches it creates a clean browser profile, this way no cookies plugins or other thing will interferer with the tests. However, this also means that no temporary certificates were being persisted from test to test!

The Solution

Create a custom Firefox profile, and tell Selenium to use that when it launches.

Close your Firefox browser and Run "firefox.exe -profileManager". This will allow you to create another Firefox profile (I named mine "Selenium"). Then launch that profile, go to the website you want to test, and store a permanent SSH certificate for it.

Above you may have noticed that I used the "-firefoxProfileTemplate" argument when launching the Selenium server, this tells the server to load the specified profile rather than creating a new one.

Note: The changes made to your profile while Selenium is running will not be saved! This is good because no cookies or other such things will be left behind to intefear with future tests. However, it has the slight draw back (for us at least) of cause Selenium to reinstall its Firefox plugins every time it runs. ...meh!

(Thanks to Chris Pitts for blogging about something very similar to this!)

Helpful Links

Selenium Homepage
Selenium IDE Homepage
Selenium Video Example/Tutorial
Selenium on Wikipedia

Real Time Web Analytics