Copy
Wakaleo Consulting - Optimizing your Development Process

The Art of Writing Maintainable Selenium Scripts

Selenium is a popular web testing framework, that works well for both regression tests and acceptance tests. It works well almost all web applications, even those using complex AJAX-based user interfaces.

One of the particularly seductive features of Selenium, at least at first, is the Selenium IDE, and it's ability to record and replay interactions with a web site, using a HTML-based scripting language called Selenese. A Selenese script takes the form of an HTML table, as shown here:

typeusernamescott
typepasswordtiger
clicklogin 
assertTextPresentHello Scott! 

The Selenium IDE does come in handy from time to time, when writing Selenium test scripts, as it can give you an initial idea of what the script should look like. However, it poses considerable maintenance problems in the medium to long term: the scripts end up containing a large amount of duplication, they do not support data-driven testing well, and the recorded XPath expressions are often sub-optimal for real-world web applcations. In fact, Selenese scripts should really never be used for serious test scripting of non-trivial real-world applications.

Indeed, it is a much better strategy to write your tests in a programming language such as Java or Groovy. This is more flexible, and makes it easier to use data-driven testing strategy, and to reuse testing logic between scripts. Using Groovy scripting, for example, the test described above might look something like this:


public class LoginPageTest extends SeleneseTestCase {

 public void setUp() throws Exception {
   setUp("http://testserver.mycompany.com/myapp", "*firefox");
 }

 public void testLogin() throws Exception {
      selenium.open("/");
      selenium.type("username", "scott");
      selenium.type("password", "tiger");
      selenium.click("login");
      selenium.waitForPageToLoad("5000");
      assertTrue(selenium.isTextPresent("Hello Scott!"));
}

This type of test is more flexible - it i However a better approach is to use a strategy often referred to as 'Page Objects'. Using this approach. you encapsulate your application pages, or other graphical components used in your application, using your preferred programming language (say Java or Groovy). This essentially involves writing a DSL for your application's user interface, which hides the complexity of the Selenium scripting behind reusable UI components that high-level test scripts can then use.

Using a Page Objects strategy, a Login page might be modeled as follows:


public class LoginPage extends AbstractPageObject {

public void enterUsernameOf(String username) throws Exception {
      selenium.type("username", username);
}

public void enterPasswordOf(String password) throws Exception {
      selenium.type("password", password);
      selenium.click("login");
      selenium.waitForPageToLoad("5000");
      assertTrue(selenium.isTextPresent("Hello Scott!"));
}

public void login() throws Exception {
      selenium.click("login");
      selenium.waitForPageToLoad("5000");
}
Your page object might also have a higher-level method, designed for reuse in other scripts:

public void login(String username, String password) throws Exception {
    enterUsernameOf( username );
    enterPasswordOf( password );
    login(); 
}

You often refactor common methods into a parent page object:


public abstract class AbstractPageObject {
    private Selenium selenium;
    private String contextPath;

    public AbstractPageObject(Selenium selenium, String contextPath) {
        this.selenium = selenium
        this.contextPath = contextPath
    }

    public void open() {
        selenium.open(contextPath)
        selenium.waitForPageToLoad("5000") 
    }

    public void shouldContainText(String text) {
        selenium.assertTextPresent(text);
    }
    ... 
}

Page objects like this are used at a higher level, to hide the potential complexity of the Selenium API calls from the test scripts themselves. They are written by developers, for those who will be writing the actual test scripts. For example, the following test requires no knowledge of the detailed page structure or of the subtleties of the Selenium API:


public void LoginTest {

    private LoginPage loginPage;

    private Selenium selenium

    @Before
    public void setupSeleniumClient() { 
        selenium = new DefaultSelenium("localhost", 4444, "*firefox", 
                                       http://testserver.mycompany.com/myapp")
        selenium.start()
        
        loginPage = new LoginPage(selenium, context);
        loginPage.open();
    }

    @After
    public void shutdownSelenium() {
        selenium.close();
    }

    @Test
    public void whenAUserLogsOnTheAppShouldDisplayAWelcomeMessage() throws Exception {
        loginPage.enterUsernameOf("scott")
        loginPage.enterPasswordOf("tiger")
        loginPage.login() 
        loginPage.shouldContainText("Hello Scott!") 
   }
}

However this approach really comes into its own when combined with a Behaviour-Driven Development, or BDD, framework such as easyb, JBehave or Cucumber. Easyb, for example, combines a readable narrative structure with the lightweight and relatively forgiving nature of the Groovy syntax to make for very intuitive tests indeed:


import com.mycompany.myapp.selenium.pages.LoginPage

scenario  "The system displays a nice welcome message", {

  given "the web site is available", {
    // Start up the Selenium RC server
  }
  when "the user enters a valid username and password", {
    loginPage = new LoginPage(selenium, context)

    loginPage.enterUsernameOf "scott"
    loginPage.enterPasswordOf "tiger"    
  }
  and "the user logs in", {
    loginPage.login() 
  }
  then "the system should display a nice welcome message", {
    loginPage.shouldContainText "Hello Scott!" 
  }
}

Using a BDD approach in this way has two significant benefits: non-developers (testers, business analysts, product owners and so on) can more actively participate in the test scripting process without needing detailed knowledge of Selenium, and the BDD reports provide excellent feedback, in terms that they can understand, about how development is proceeding.

Training news

Don't forget to check out the upcoming Testing and TDD for Java Developers course in SydneyWellingtonAuckland and Canberra.

When done well, agile developer testing practices can have a huge and lasting impact on higher code quality, better designed and documented code, more relevent code, and reduced defect rates. This is a fun, practical workshop packed with labs, where you will using TDD/BDD and a host of testing tools on a real application, to develop real features, with plenty of real-world tips and tricks.

We will also be running our comprehensive Java Power Tools Bootcamps soon in Wellington and London with other sites to be announced soon! This is a full-on course where every developer is guaranteed to learn not just one new technique, but a range of tips, tricks and tools that you can use across the board to speed up your development and set up a kick-ass development infrastructure.

Upcoming Bootcamps
There are still places available on the following Java Power Tools Bootcamps - book early to avoid disappointment!
Wellington - 30 Aug - 3 Sept 2010
London - 13-17 Sept 2010

TDD Workshops

Learn more about the most effective ways to design and test your applications at the new Testing and TDD for Java Developers workshop!
Sydney - 20-21 May 2010
Wellington - 27-28 May 2010
Auckland - 3-4 June 2010
Canberra - August 2010

Unsubscribe <<Email Address>> from this list.

Copyright (C) 2010 Wakaleo Consulting All rights reserved.

Forward this email to a friend
Update your profile
Email Marketing Powered by Mailchimp