Cucumber and selenium

Cucumber is a kind of language that specifies so called features in order to specify a test case or scenario. It can run automated tests written in a behavior-driven development style. Cucumber is written in Ruby and since 2011 their is a Java port as well. An example feature is as follows:

Feature: Planets in universe

  Scenario: The universe holds 8 planets
    Given I have a universe
    When I step into a space plane
    And I travel from the sun to the end of the universe
    Then I pass the orbit of 8 planets

Implementation of this feature will follow by some pseudo code and explanation by text. At the end one will have to do it in order to learn it…

 

First of all lets start by describing the maven dependency for cucumber. The implementation of cukes is utilized. The version is interpolated, at the time of writing 1.1.7 was the latest and greatest.

<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>${version}</version>
</dependency>
<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-core</artifactId>
    <version>${version}</version>
</dependency>
<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>${version}</version>
</dependency>

In addition with Selenium one can really do some nice automated testing. In one of my last projects I created a simple BrowserDriver as follows.

public class BrowserDriver {

    private static final Logger LOGGER = Logger.getLogger(BrowserDriver.class);

    private static WebDriver mDriver;

    public synchronized static WebDriver getCurrentDriver() {
        if (mDriver == null) {
            try {
                FirefoxProfile ff = new FirefoxProfile();
                ff.setPreference("network.proxy.type", Proxy.ProxyType.AUTODETECT.ordinal());
                mDriver = new FirefoxDriver(ff);
            } finally {
                Runtime.getRuntime().addShutdownHook(
                        new Thread(new BrowserCleanup()));
            }
        }
        return mDriver;
    }

    private static class BrowserCleanup implements Runnable {
        public void run() {
            LOGGER.info("Closing the browser");
            close();
        }
    }

    public static void close() {
        try {
            getCurrentDriver().quit();
            mDriver = null;
            LOGGER.info("closing the browser");
        } catch (UnreachableBrowserException e) {
            LOGGER.info("cannot close browser: unreachable browser");
        }
    }

    public static void loadPage(String url) {
        LOGGER.info("Directing browser to:" + url);
        getCurrentDriver().get(url);
    }
}

Selenium can work with so called page objects In order to keep the number of page objects plain and simple I decided to create an interface as follows to imply the isDisplayed() the same as Selenium does.

public interface Page {
    public boolean isDisplayed();
}

And an example page object.

public class MyRetiredPage extends LoggedInPage {

    @FindBy(how = How.XPATH, using = "//*[@id='mainContainer']/h2")
    private Webelement myH2;

    @Override
    public boolean isDisplayed() {
        if (isRetired()) {
            WebElement submit = BrowserDriver.getCurrentDriver()
                    .findElement(By.className("submitButton"));
            submit.click();
        }
        return myH2.isDisplayed();
    }

    public boolean isRetired() {
        try {
            return BrowserDriver.getCurrentDriver()
                    .findElement(By.xpath("//*[@id='mainContainer']/h3"))
                    .getText().equalsIgnoreCase("Is retired...");
        } catch (NoSuchElementException e) {
            return false;
        }
    }
}

Please note the @FindBy annotations, pretty cool stuff with all kinds of elements in order to match and inject. To keep the code clean I decided to create a simple strategy so the actual page objects are loaded in runtime. The example strategy is as follows.

public class PageStrategy {

    static Map<String, Page> PAGES = new HashMap<String, Page>() {{
        put(HomePage.class.getName().toLowerCase(), new HomePage());
        put(LoggedInPage.class.getName().toLowerCase(), new LoggedInPage());
    }};

    public Page getPage(String name) {
        final Page page = PAGES.get(name.toLowerCase());
        PageFactory.initElements(BrowserDriver.getCurrentDriver(), page);
        return page;
    }
}