Thursday, 3 June 2010

Combining xUnit.net and Selenium

There has been a fair amount of discussion about how to improve our test automation here in the Web Team at Red Gate. One of the avenues that we are keen to go down is browser automation.

In the browser automation arena, the two big players for .NET web applications are WATIN (Web Application Testing In .Net) and Selenium. In a recent blog post, Ben Hall describes an xUnit attribute that allows you to run WATIN tests across multiple browsers, simply by providing the desired browser as an argument to your test method.

I took Ben’s code for a test drive, and really liked how concise the resulting tests were. However, there are well-documented advantages to using Selenium as your browser automation framework, so I decided to have a go at re-writing Ben’s [Browser] attribute to target Selenium instead.

Writing your own xUnit attribute is deliberately quite straightforward. Each attribute’s abstract base class is public, allowing you to easily write your own implementation. In this case we’re interested in the DataAttribute class.

The DataAttribute class only contains one abstract method that needs overriding, namely:

public abstract IEnumerable<object[]> GetData(MethodInfo methodUnderTest, Type[] parameterTypes);

So our implementation needs to return an object[] containing the selenium browser object to pass into our test:

public class BrowserAttribute : DataAttribute
{
 
private ISelenium Browser { get; set; }
 
public BrowserAttribute(string browser)
 
{
   
string url = "http://www.google.co.uk";
   
switch (browser)
   
{
     
case "Internet Explorer 7":
     
Browser = new DefaultSelenium("localhost", 4444, "*iexplore", url);
     
Browser.Start();
     
break;
     
case "Firefox 3.5":
     
Browser = new DefaultSelenium("localhost", 4444, "*firefox", url);
     
Browser.Start();
     
break;
     
case "Google Chrome":
     
Browser = new DefaultSelenium("localhost", 4444, "*googlechrome", url);
     
Browser.Start();
     
break;
     
case "Opera":
     
Browser = new DefaultSelenium("localhost", 4444, "*opera", url);
     
Browser.Start();
     
break;
   
}
  }
 
public override IEnumerable<object[]> GetData(MethodInfo methodUnderTest,
Type[] parameterTypes)
 
{
   
return new[] { Browser };
 
}
}

The above implementation is enough to allow us to write nice clean tests, like this (note that I pass the iSelenium parameter into the Browser object purely for post test clean up purposes, which I won’t worry about showing here):



[Theory]
[
Browser("Internet Explorer 7")]
[
Browser("Firefox 3.5")]
[
Browser("Google Chrome")]
[
Browser("Opera")]
public void Google_For_SimpleTalk(ISelenium iSelenium)
{

    Browser = iSelenium;
    Browser.Open("/");
    Browser.Type("q", "Simple Talk");
    Browser.Click(
"btnG");
    Browser.WaitForPageToLoad(
"5000");
    
Assert.True(Browser.IsTextPresent("www.simple-talk.com"));
}



This version of the Selenium [Browser] attribute is obviously not very robust, as it has the URL of the Google homepage hard-coded. Moreover, it is currently only able to take advantage of the browser versions currently installed on my local machine. Whilst these versions are the most popular amongst the visitors to our sites, they are by no means the only ones. Another drawback is that, in its current form, the attribute causes all the requested browser instances to be created before any of the executions of a test case occur.

Once I had this basic version up and running, my next task was to extend it to address these issues, so that we can begin to utilise it in our production test environment. In my next post I’ll go through how I did that!

No comments:

Post a Comment