Mark Keats

Software Engineer and UX/IA Pro

ASP.NET Testing with Selenium

June 18th, 2009

When I started my first job out of university back in 2003 there were very few testing tools for ASP.NET, and the ones that were around didn’t really work well. Since that time I have seen web application testing systems crop up from time-to-time, but none of them have ever really stood out as working how I’d like them to.

A week ago a colleague sent me a link to Selenium, which is a new web application testing system that I have been looking at. It allows you to build tests like you would record macros in Office, then run them from NUnit. Selenium also allows you to run tests on other operating system and browsers, such as Safari on Mac OS X.

Selenium has 3 parts of interest to anyone looking to create ASP.NET tests:

  • Selenium IDE
    The selenium IDE is a Firefox plug-in that allows you to record tests, in the same way that you would record a macro in Office, and play it back.
  • Selenium RC
    Selenium remote control is a command line server on your system (or a test system) and allows Selenium tests to be run from outside the IDE – for example in an NUnit test. Selenium RC is a Java app, so it can run on an Apple Mac to provide access to run Safari tests.
  • Selenium C# Library
    A C# library that provides access Selenium for NUnit tests.

Getting Started

To get started using Selenium you will need to download the Selenium IDE from here. This is a Firefox plug-in so make sure that you are using Firefox when you visit that link. Once you have installed the plug-in and re-started this part is ready to go.

You will also need Java installed to run Selenium RC. This can be downloaded from here.

Building your First Test

Selenium IDE Record ButtonOpen Firefox and click Tools > Selenium IDE to open the IDE window. The IDE will immediately start recording your actions once open, as indicated by the ‘Now Recording’ button in the top right of the IDE being depressed. Selenium will convert all clicks, text inputs, page loads, etc. into a list of commands that can be played back.

Let’s start by writing a simple test. For this post I created a test for changing a user’s profile first and last name. With the Selenium IDE running and recording my actions I performed the following in my application:

  1. Click on ‘My Account’
  2. Change the First and Last Name to Joe Bloggs.
  3. Click ‘Save’
  4. At the top of the application I select the ‘Joe Bloggs’ text that displays the current user’s name and right-click to select Show All Available Commands > assertText //div[@id='actions']/b (where this XPath will return the <b> element containing the current user’s name).

Your IDE window should now look like this:

Selenium IDE Test

Click the ‘Now Recording’ button in the top right of the IDE to stop it recording any more of your actions. You now have your first test. Clicking  the green play button in the IDE will run the test in the browser. You can use the IDE to insert new commands and edit the existing ones as needed to tweak and refine your test.

Once you are happy that the test works you click on the ‘Source’ tab in the IDE and then select Options > Format > C# from the menu. The IDE will now produce the C# NUnit test for you automatically.

Creating the NUnit Test

To use this Selenium test in an NUnit test there are a few things you can do to make things simpler, especially if you need to perform a login before you can run your tests. You can include the SetUp and TearDown methods in each of your test classes, but I created a base test class that has these methods in it. In the set-up method I perform the login actions so that any test I write will automatically be working in a logged-in environment.

public class TestBase
{
protected ISelenium selenium;
protected StringBuilder verificationErrors;

[SetUp]
public void SetupTest()
{
selenium = new DefaultSelenium("localhost", 4444, "*chrome", "http://localhost/");
selenium.Start();
verificationErrors = new StringBuilder();
Login();
}

[TearDown]
public void TeardownTest()
{
try
{
selenium.Stop();
}
catch (Exception)
{
// Ignore errors if unable to close the browser
}
Assert.IsTrue(verificationErrors.ToString() == string.Empty);
}

/// <summary>
/// Performs a login to the application.
/// </summary>
public void Login()
{
selenium.Open("/Login.aspx");
selenium.Type("//input[@testid='Username']", "Administrator");
selenium.Type("//input[@testid='Password']", "Password");
selenium.Click("//a[@testid='Login']");
selenium.WaitForPageToLoad("30000");
}
}

I have cleaned-up the test method and also added some code to restore the first/last name to their original values once the test has run. This method is in a test class that descends from my base test class. Here is the result:

[TestFixture]
public class ProfileTests : TestBase
{
[Test]
public void ChangeProfileName()
{
selenium.Open("/Home.aspx");

// Click on 'My Account'
selenium.Click("ctl00_HeaderNavigation1_myAccountLink");
selenium.WaitForPageToLoad("30000");

var firstName = selenium.GetValue("ctl00_ContentPlaceHolder1_ctl00_firstNameTextBox");
var lastName = selenium.GetValue("ctl00_ContentPlaceHolder1_ctl00_lastNameTextBox");

// Enter new first/last name and save.
selenium.Type("ctl00_ContentPlaceHolder1_ctl00_firstNameTextBox", "Joe");
selenium.Type("ctl00_ContentPlaceHolder1_ctl00_lastNameTextBox", "Bloggs");
selenium.Click("ctl00_ContentPlaceHolder1_ctl00_saveLinkButton");

// Verify name has changed.
selenium.WaitForPageToLoad("30000");
Assert.AreEqual("Joe Bloggs", selenium.GetText("//div[@id='actions']/b"));

// Restore Data
selenium.Click("ctl00_HeaderNavigation1_myAccountLink");
selenium.WaitForPageToLoad("30000");
selenium.Type("ctl00_ContentPlaceHolder1_ctl00_firstNameTextBox", firstName);
selenium.Type("ctl00_ContentPlaceHolder1_ctl00_lastNameTextBox", lastName);
selenium.Click("ctl00_ContentPlaceHolder1_ctl00_saveLinkButton");
}
}

Running The NUnit Test

To run the test you first need to start the Selenium RC server on your local system. If you have Java installed all you need to do is run java -jar selenium-server.jar. With the server running you just run the NUnit test in your normal test runner. When the test runs you will see a new instance of Firefox open up and the test will run inside this. The output and result of your test will appear like any other ‘normal’ NUnit test in your runner:

ReSharper Test Runner Success

Control IDs and Possible Problems

One thing you will notice is that by default the Selenium IDE creates tests using the ID of a control to identify it. This may be a problem in ASP.NET as the ID of a control when it is rendered can change if the control hierarchy on a page changes for any reason – maybe dynamically added controls or UI design modifications.

To solve this problem I wrote an extension method for WebControl called AssignTestID. This will add a testid=”blah” attribute to the HTML control, which we can use to identify it in our tests:

public static void AssignTestID(this WebControl ctrl)
{
ctrl.AssignTestID(ctrl.ID);
}

public static void AssignTestID(this WebControl ctrl, string testId)
{
ctrl.Attributes.Add("TestID", testId);
}

Then update our NUnit test with:

selenium.Type("//input[@testid='FirstName']", "Joe");
selenium.Type("//input[@testid='LastName']", "Bloggs");
selenium.Click("//a[@testid='Save']");

You can also use any other XPath to select an element, but it really depends on your page layouts as to what would be suitable for your application . A good tutorial on XPath can be found here.

Written by Mark Keats

No comments

Posted in Uncategorized

Leave a Reply