Get started with Selenium

Last post of a three posts series about testing, after Unit Testing and Integration Testing, let’s talk about UI testing.

Introduction

Selenium is a Browser driver, basically it does what you would do if you were manually testing, i.e. clicking, looking for elements, selecting, scrolling…, except it does it alone, and can do it for hours without a break, every night…

How it works is quite simple, you are targeting DOM elements (or node, or WebElement, or any name you would like to call them) with selectors, and then you can do various operations on those like a user would do: wait for them to be present, to be visible, to be clickable, click on them, drag and drop something, enter information in a text field, …

Keep in mind that Selenium does the same thing that you would do. So if an element is not visible on the page and you would scroll to catch it before clicking, you have to do the same with Selenium, or it will throw an exception saying the element is not clickable because it is out of the view, which makes sense.

This article is an introduction to Selenium, if you want more applied examples for ICN and Dojo, you can read this post, but I would recommend you to read this one first to get familiar with Selenium.

Get started with Selenium


If you are using a project management system like Maven, you just have to add Selenium to your dependencies. If not here are the list of jars needed to make Selenium works:

  • commons-exec-1.1.jar
  • commons-logging-1.1.3.jar
  • (cucumber-core-1.2.0.jar)
  • (cucumber-java-1.2.0.jar)
  • (cucumber-junit-1.2.0.jar)
  • (cucumber-jvm-deps-1.0.3.jar)
  • gherkin-2.12.2.jar
  • gson-2.3.jar
  • guava-18.0.jar
  • httpclient-4.3.4.jar
  • httpcore-4.3.2.jar
  • selenium-api-2.44.0.jar
  • selenium-chrome-driver-2.44.0.jar
  • selenium-firefox-driver-2.44.0.jar
  • selenium-java-2.44.0.jar
  • selenium-remote-driver-2.44.0.jar
  • selenium-support-2.44.0.jar

I also added the Cucumber jars, this is another subject, which I will introduce in another post, but this is nice to use with Selenium. Because Selenium is really close to the user experience, and Cucumber allows us to translate in a human readable language what we will do as a user, they work great together. You won’t really need Cucumber knowledge for this post, but I’ll recommend you to read the Cucumber post after this one, it will be really quick.

Selenium development pattern and good practices

Before starting to code and introduce some simple examples, let’s understand what is the good practice with Selenium.

The good practice here is to have a strict distinction between how to target element on the page (we will call it Container), and higher level methods actually doing things, like Log In, Log Out, … (we will call it View). The good practice will be to not have any selectors in the view. This way if the project is updated, we will have to change only the containers. We can even put all selectors strings into properties files but this is not mandatory since it is test and can be easily recompiled anyway.

To summarize, we will have:

  • The Container containing how to find element on the page (it contains all selectors)
  • The view for higher level methods. It uses the element from the container to interact with (click, fill, move, …)

How to interact with a browser

Well before starting, we also need to see how to interact with a browser, this is, after all, the main purpose of Selenium :). Selenium interacts with the browser via a Driver. This is the only entry point for Selenium to interact with the Browser. It exists plenty of Drivers, for instance for Firefox (not working for version newer than 27 for now), Chrome, Android, Safari, iOS, …

This is great because since the Driver is the only entry point, and implement an interface, you will have nothing to change in your code to test against another browser, except changing the implementation used (well maybe for mobile browser where the structure of the site may change but that’s another story).

Here is an example on how to test against Firefox:

log.info("Using Firefox");
System.setProperty("webdriver.firefox.bin", "C:\\Program Files (x86)\\Mozilla Firefox 27\\firefox.exe");
mDriver = new FirefoxDriver(getFirefoxProfile());

With the getFireFoxProfile() method as follow

private static FirefoxProfile getFirefoxProfile() {
    FirefoxProfile firefoxProfile = new FirefoxProfile();
    try {
        firefoxProfile.addExtension(new File("firefox-plugins\\firebug-1.12.8.xpi"));
        firefoxProfile.addExtension(new File("firefox-plugins\\firefinder-1.4.xpi"));
    } catch (IOException e) {
        e.printStackTrace();
    }
    firefoxProfile.setPreference("extensions.firebug.currentVersion", "1.12.8");  // Avoid startup screen
    return firefoxProfile;
}

And this is quite long because we want to

  • Tell the Driver where are the Firefox binaries (because it doesn’t work with my default version 33)
  • Launch Firefox with a few add-ons making debugging fun (no I’m joking but at least it saves you some time)

Using your default Firefox installation and no plug-ins it would be like this. Simple no?

mDriver = new FirefoxDriver(new FirefoxProfile());

And finally here is the example with Chrome:

log.info("Using Chrome");
System.setProperty("webdriver.chrome.driver", "test/chromedriver.exe");
mDriver = new ChromeDriver();

For Chrome, you need an exe file (or equivalent for other platform) to drive Chrome. They can be downloaded here.

How to wait element

When you are testing UI, one of the most important thing is to learn to wait! Indeed waiting for the page to load isn’t enough in most situations, especially in RIA and let’s not talk about ICN where everything is loaded via Ajax. Therefore we need a more advanced way to wait and tell when what we want is ready.

In selenium you have two kinds of wait. The implicit wait and the explicit wait. There are two concepts quite distinct, and we might be tempted to use implicit wait everywhere and increase the implicit time-out because the syntax is nicer. But that’s a really bad idea (I know I did it :)) and we will see later why.

Implicit wait

The implicit wait is used when using the findElement and findElements methods on WebElement (or their corresponding annotation @FindBy(s)). It usually waits a really short time (default is 2 seconds). This wait is to be used because the browser needs a few ms to answer to the driver, or also sometimes you have some quick JavaScript executed before getting the element. However it should NOT be used to wait something to be loaded from the server, even in Ajax. You are supposed to use this methods when you know the element you are targeting is already on the page. You need to analyze how your application works, when things are loaded from the server (server round-trip applied) and then use explicit wait.

Explicit wait

As I said, this kind of wait is the one you want to use when you are waiting for something being fetched from the server. For instance when you are expanding a node in ICN, there is a server round-trip applied (an ajax call), and you can not wait the node’s children with a findElements call, you need something more patient. This is what the explicit wait is for. When you create an explicit wait, you set the time-out you are willing to wait. The code looks like that:

WebDriverWait wait = new WebDriverWait(mDriver, 60);
wait.until(...);

You can wait either for pre-defined condition, or any function implementation you want.

Example with a pre-defined condition:

WebDriverWait wait = new WebDriverWait(mDriver, 60);
wait.until(ExpectedConditions.visibilityOf(elementToWaitFor));

We are waiting 60 for an element to be visible on the page. Check the ExpectedConditions class to see what is available.

Example with a custom function

WebDriverWait wait = new WebDriverWait(mDriver, 60);
wait.until(new Function() {
    public Object apply(Object arg0) {
        return e.getAttribute("class").matches(".*\\b" + cssClass + "\\b.*");
    }
});

In this example we are waiting for a css class to be present (you will see, especially with ICN and Dojo in general, we will work a lot with css class because they give good information about the node state. While the function returns null, false, or throw an exception the wait continues. As soon as the function returns an object or true, the wait finishes.

Why not always implicit waits

I told you at the beginning to absolutely not always use implicit waits and increase the implicit time-out, for example to 60 seconds. Well now let’s see why. Usually when you are testing, you are often also testing something is not on the page, for example menus, error messages and so on. The only way to check if something is present with Selenium it to use a findElement and wait for it to fail. If you do use only implicit wait with a 60 seconds time-out, let’s imagine you want to check 5 menus are absent, it will take 5 minutes while it is expected and not a failure at all. If you have a lot of test like that, your Selenium test might take month to run :).

Containers and Selectors

Containers

Now we said that we split the design in two distinct parts, Containers and Views, let’s see how to write a basic Container. A basic container look like that:

public class LoginContainer {

    @FindBy(id = "loginInputField")
    public WebElement loginInputField;

    @FindBy(id = "passwordInputField")
    public WebElement passwordInputField;

}

Of course this is the most basic example and the truth is it will never be used with Dojo because id are not fixed. But this is the main idea, you have a few fields with FindBy or FindBys annotations allowing you to bind the field to actual element on the page.

Selectors

Now let’s see what tools we get to target element, because we won’t go far with id selectors.

You can use any classic css selector, Selenium also add a few custom css selectors quite useful that you can find in their documentation.

Here is an example:

@FindBy(how = How.CSS, using = ".inlineMessageError")
public WebElement errorMessage;

You also have a few other option, like tag, name, but I want to focus on the most powerful one, which we will need with Dojo and ICN, XPath.

Here is an example:

@FindBy(how = How.XPATH, using = "//div[@class = 'field' and contains(label/text(),'User name')]/div[contains(@class, 'dijitTextBox')]/div[contains(@class, 'dijitInputField')]/input")
public WebElement usernameInputField;

This is more complex, but this is a lot smarter, because we don’t depend on any id, and we event look in one sub-branch (where the label is) and go back up then down to find the input field. You can do pretty much anything you need with XPath, I’ll let you refer to the documentation.

Finally I just wanted to add that the supported version of XPath depends on the browser note on Selenium, so stick to XPath 1.0 for now because only Firefox support XPath 2.0 (even if XPath 2 is really nice :))

Particularity of the @FindBy(s) annotations

There is one specificity on these annotations, as it is used right now, it will go find the element into the page, asking the driver every time we are using the field. This is usually what we want in heavy ajax application where node can be replace easily, because if you keep the same instance but it is replaced in the page (even if the new one is exactly the same), then it will fail. I would recommend you to keep it this way, since usually performances don’t really matter in test development, but if you are sure your node never changes (like banner or navigation menu), you can use the @CacheLookup annotation.

 Views

Now that we have containers to target elements and that we know how to wait for them, we want to do things with those elements. This is what Views are for. In the view you need to instantiate your container is a way that Selenium binds it to the actual page. This is done with PageFactory object like this:

public class LoginView {
    private static final LoginContainer loginContainer = PageFactory.initElements(BrowserDriver.getCurrentDriver(), LoginContainer.class);
}

And we can then call action on WebElement, like click,  sendKeys and everything you need. Here is an example of view:

public class LoginView {
    private static final LoginContainer loginContainer = PageFactory.initElements(BrowserDriver.getCurrentDriver(), LoginContainer.class);

    private static final Logger log = LoggerFactory.getLogger(LoginView.class);

    public static void isDisplayedCheck() {
        log.info("Checking login page is displayed");
        BrowserDriver.waitForElement(loginContainer.usernameInputField);
        BrowserDriver.waitForElement(loginContainer.passwordInputField);
    }

    public static void login(String username, String password) {
        log.info("Logging in with username: {} password: ******", username);
        loginContainer.usernameInputField.clear();
        loginContainer.usernameInputField.sendKeys(username);
        loginContainer.passwordInputField.clear();
        loginContainer.passwordInputField.sendKeys(password);
        loginContainer.submitButton.click();
    }

    public static void checkLoginSuccess() throws Exception {
        log.info("Check login was successful");
        HomeView.isDisplayedCheck();
    }

    public static void checkLoginErrors() {
        log.info("Check login errors displayed");
        BrowserDriver.waitForElement(loginContainer.errorMessage);
    }
}

 

Leave a Reply