You are on page 1of 12

Notes on Selenium WebDriver

Introduction
Selenium WebDriver (also called Selenium 2) is a second generation web application test facility. It is the result of
efforts of Google, ThoughtWorks, and other development groups, as well as prior technology such as WatiN.
Development on Selenium 2 started about two years ago with a different model of how to operate in the browser.
Selenium 2 is a combination of prior Selenium development plus a new tool called WebDriver. This removes the
need for starting up the Selenium server, and allows the instrumented browser to interact with the application.
The API for writing Selenium WebDriver tests is much more object-oriented, with objects for page elements and
entire pages. Hence, there isn’t a big list of method calls on the main Selenium object, but a set of objects that are
created using methods on the Driver object (the equivalent of the main Selenium object), and then methods on those
objects to perform actions.
Selenium 2 came went GA in early-mid 2011, and is now at version 2.19.0, released early February 2012. It
includes a compatibility API with the prior Selenium API, which can help with migration, though that is not
discussed here. It is recommended that new development use Selenium 2, with either of the API’s but moving
toward Selenium 2.

Browser specific drivers
The WebDrivers are browser-specific implementations of the Selenium 2 API. Unlike Selenium 1.x, which relied on
a single JavaScript implementation for all browsers to interact with their page elements, and so was limited by what
could be achieved through JavaScript, the WebDrivers interact with the browsers in the best way possible for that
browser.
What this in turn allows is:
1. Native keyboard and mouse interactions. This mimics the end user interactions with the browser much
better than what earlier versions of Selenium provided.
2. Problems related to same origin policy in Selenium 1 are now gone.
3. Better support for handling popups and dialogs. This includes some big problem items like upload and
downloads.

Mobile web support
Selenium 2 now supports running tests in mobile devices. IPhone and Android are the two platforms supported.
The way it works is that you have to install an app (a mini selenium server) on the device. This listens for
commands which then drive the web browser within the app.
This mechanism however will not allow us to test native apps in these devices.

Resources
No specific books as yet, you must rely on web-delivered documents.
Primary documentation is at http://seleniumhq.org/docs/03_webdriver.html
Good overview at http://code.google.com/p/selenium/wiki/GettingStarted
A Reference card for Selenium 2 is available at http://refcardz.dzone.com/refcardz/getting-started-selenium-20

Example of Use
The primary classes are WebDriver and WebElement.

Page 1

getTitle(). you could perform similar steps with Selenium 1. Page 2 .get("http://www.submit().com/p/selenium/wiki/RemoteWebDriver.google. See http://code. Except for the WebElement class. such as FirefoxDriver are simply preconfigured versions of RemoteWebDriver. Finally. WebElement queryField = driver. } In this example. Driver Implementations WebDriver class is available in several implementations. driver. Both of these class structures are discussed in the sections below.google. we create a WebDriver. The most general is RemoteWebDriver.findElement(By.sendKeys("cats"). we check that the page title has changed to contain the expected value. there is much more flexibility because of the various implementations of WebDriver (for different browsers). containsString("cats")). however. assertThat(driver. then get a page.com"). queryField. Here is the API: With the Capabilities interface defined as: This enables you to set up Firefox or other runtime environments. The various subclasses of RemoteWebDriver. and the operations on the WebElement provide a good abstraction. queryField. which allows for the programmatic specification of the test browser and its runtime. then find an element and perform operations on it. including a headless front-end.name("q")).public void theUserShouldBeAbleToTypeInQueryTerms() { WebDriver driver = new FirefoxDriver().

com/p/selenium/wiki/HtmlUnitDriver).The headless front-end typically uses HtmlUnitDriver (see http://en. Instead. Locators include id.org/wiki/HtmlUnit).0. we are using Firefox 4. Hence. Version Compatibility Apparently there is an update required to the WebDriver library for most updates to browsers such as Firefox. css. xpath. and link text.com/questions/7733824/selenium-webdriver-firefox-7-0-1-incompatibility for a discussion.google. but matches the 2. class. which is not entirely the same as current production browsers. Page Interaction Model Built around instances of WebElement. Please note some of the restrictions on use of JavaScript in this (it is simply running Rhino as a JavaScript engine. which dates back to early 2011.0 version of the WebDriver library.wikipedia. Methods on WebElement include: Page 3 . such as Firefox 10 during early 2012. we found that we often could not update to new browsers.8. See http://stackoverflow. See http://code. These can be created by locating an element on the page.

you can search for an element within a given element’s scope: findElementBy(By by) and findElementsBy(By by). There are special operations to handle select elements on your web page: Page 4 .Plus.

WebElement myDynamicElement = driver.q = driver. There are some convenience methods provided that help you write code that will wait only as long as required.html This describes two types of waits: An explicit waits is code you define to wait for a certain condition to occur before proceeding further in the code. and by css.findElement(By. For instance: public class GoogleSearchPage { protected WebDriver driver. TimeUnit.get("http://somedomain/url_that_delays_loading").SECONDS). }}). which also offers position information (size. WebDriver driver = new FirefoxDriver(). WebDriver driver = new FirefoxDriver(). this. WebElement myDynamicElement = (new WebDriverWait(driver.name("q")).until(new ExpectedCondition<WebElement>(){ @Override public WebElement apply(WebDriver d) { return d. driver. private WebElement q.manage(). public GoogleSearchPage(WebDriver driver) { this. WebDriverWait in combination with ExpectedCondition is one way this can be accomplished. private WebElement btnG. etc.name("btnG")).driver = driver.get("http://somedomain/url_that_delays_loading"). which sets the condition to an exact time period to wait.implicitlyWait(10.). driver. the implicit wait is set for the life of the WebDriver object instance. 10)) . } Page 5 . and ways to find these elements include by name. Note that ExpectedCondition depends upon the Google Guava library. Handling Waits See http://seleniumhq. This API is ideal for writing Page objects. as the WebDriver itself doesn’t wait (since the click may be an ajax update) Page Object Model A WebElement is an element in the DOM of a page. click at location.btnG = driver.id("myDynamicElement")).findElement(By.findElement(By. by id. driver.You can create an instance of a Select from a WebElement that is known to represent a select element: There is a subclass of WebElement called RenderedWebElement. this. These waits are needed for clicks on the page elements. as you can look up the WebElements as the page is opened. The default setting is 0. Once set.sleep().org/docs/04_webdriver_advanced.id("myDynamicElement")).findElement(By.timeouts(). The worst case of this is Thread. An implicit wait is to tell WebDriver to poll the DOM for a certain amount of time when trying to find an element or elements if they are not immediately available.

searchButton. @FindBy(name="btnG") private WebElement searchButton. if the page is created by the PageFactory’s initElements method (see below). } public void searchFor(String searchTerm) { searchField. @FindBy(id="q") private WebElement searchField. This is similar to the verifyPage() behavior in other page models. } public void clickOnSearch() { btnG. rather than performing init yourself.sendKeys(searchTerm).getTitle().click().sendKeys(searchTerm).click(). } public void open(String url) { driver. public AnnotatedGoogleSearchPage(WebDriver driver) { this.sendKeys(searchTerm). btnG. } public void close() { driver. For example: public class GoogleSearchPage { protected WebDriver driver.get(url). } public String getTitle() { return driver.click(). } public void searchFor(String searchTerm) { q. } Page 6 .quit(). } public void close() { driver. } public String getTitle() { return driver.driver = driver.get(url).getTitle().quit(). A further refinement is that you can specify the lookup of the WebEements by annotation.public void open(String url) { driver. } } You could also change the constructor to throw an exception if a required element is not present. } public void typeSearchTerm(String searchTerm) { q.

8. The plug-in provides a class that is a wrapper for the page driver. Tips and Suggestions There are no specific Page Object superclasses provided.setJavascriptEnabled(false). } Page 7 . Support for JavaScript when using HtmlUnit See http://stackoverflow. So the constructor can carry out a set of direct element lookups as part of verify() sequence. then fill out each of the elements. called WebDriverPage. extending from WebDriverPage.browser=firefox test­app –functional The Grails environment (as of Grails 1. Question: there is a NoSuchElementException.click(). } } Would be created using GoogleSearchPage page = PageFactory. For each page that you want to interact with in a test. code the following: @Before public void openXXXPage() { WebDriver driver = webdriver. GoogleSearchPage. released November 2011. as it handles the initialization of those objects for which the findElement processing is useful. the PageFactory will create the class (running the constructor). The command to run functional tests is: grails test­app –functional To request the Firefox browser instead of HtmlUnit. To disable JavaScript when using HtmlUnit. or classed elements. In the above example. you create a Page class.1.7) supports jUnit 4. then have the PageFactory carry out additional setup and checking.initElements(driver. } public void clickOnSearch() { searchButton.driver.3. To change this behavior. id’d. The PageFactory is your friend. Using WebDriver through the WebDriver Grails Plugin The current version of the plug-in is 0. each element on the page is looked up each and every time a method is called upon it. either in a direct call or in a call from the PageFactory. use: grails ­Dwebdriver.3. simply annotate the field with the {@link CacheLookup}. so create your own.public void typeSearchTerm(String searchTerm) { searchField. By default. When is this thrown? Answer: when findElement fails.3. This includes all pages with specific named. if (driver instanceof HtmlUnitDriver) { ((HtmlUnitDriver) driver).sendKeys(searchTerm).com/questions/7531818/javascript-processing-in-selenium-and-htmlunit That points out that little JavaScript support exists.class).

org/refactor/grailswebdriver/src/9fad4997c4cc/test/unit/pages/test/TestOnePage. However.groovy. The tests then just interact with these page objects. see https://bitbucket. Essentially. LinkElement offers the clickTo() method which takes a class object. a return an instance of the specified page class (which would be for the destination of the link). and call the superclass method then code your verification steps. The full list of such classes is shown below: Page 8 . using a subclass of WebDriverPage is like always instantiating your page with the PageFactory at the Java level. the checks for elements.xxxPage = webdriver. For documentation on the various specific subclasses of web element. It supports an expectedTitle value. XXXPage. and an elements specification.class) } Page Abstraction Object Model WebDriverPage is a very handy class as is supports a number of page-definition and validation features that you would otherwise have to write yourself. etc. Subclasses of WebDriverPage have properties that allow access to form fields and text on the page and methods that you call to jump to other pages. you could define a constructor for the page. WebElement Object Model Provides a set of classes for the WebElements and provides an automatic way to create pages such that the Driver is passed in and the PageFactory is called. For verification.open('/'. The clickTo method will click the link. and carry out specific findElement calls to create WebElements. you can override verify(boolean newPage). Reviewing the source of WebDriverPage showed us how it organizes the checks for title. This provides an even higher-level API (or at least more syntactically shorter) to the WebDriver and page objects. If you change your HTML around (change the ID of an element. or add an extra div for example) you only have to modify a specific part of the page object and not every test that interacted with that element. For instance. making assertions and navigating around your application.

you can use a TableElement to access the data inside. "Used"].xpath("td[1]"))         title(By.books.books. listBooksPage. "Type".Most of these are subclasses of WebDriverPageElement. NavElements include Links. Here is the test:     @Test     public void testList() {         ListBooksPage listBooksPage = webdriver.xpath("td[3]"))         type(By. the BookRow represents a single <tr> within the table.open('/books/list'. ListBooksPage)         assertEquals(["Id".columnHeaders)         assertEquals(1. "Title".books.rows[0].title)     } Here is the page: class ListBooksPage extends WebDriverPage {     static expectedTitle = "Book List"     TableElement<BookRow> books     static elements = {         books(By.                        listBooksPage. In this example.xpath("td[2]"))         author(By. "Author".xpath("//table"))     } } class BookRow extends WebDriverPageElement {     String id     String title     String author     String type     String used     static elements = {         id(By.size())         assertEquals("Cryptonomicon". and implement the following: If you have a table with known columns. listBooksPage.xpath("td[5]"))     } } Page 9 .xpath("td[4]"))         used(By.

plugins. Valid columns are 0.findElement(By.size() == 0) {             throw new IllegalArgumentException("Row ${row} has no columns")         }         if (col < 0 || col > columns.xpath("tbody/tr").get(0) ?: WebElement})     }     public int size() {         return getRowCount()     }     public int getRowCount() {         rows. Valid rows are 0.${col} in the table")     }     public WebElement getLink(int row.findElements(By.selenium.WebElement public class TableElement<T> extends WebDriverPageElement {     List<String> columnHeaders     List<T> rows     static elements = {         columnHeaders(By. listElement:                {config.xpath("tbody/tr[${row + 1}]/td[${col + 1}]"))         if (cells.size()     }     public T getAt(int row) {         rows.${size ­ 1}")         }         List columns = webElement.findElements(By.codehaus.xpath("thead/tr/th"))         rows(find: By. int col) {         def cells = webElement.tableRowElement ?: config.grails.By import org.${columns.Here is the source for TableElement: package org.findElements(By.xpath("td"))         }     } } Page 10 .selenium...genericTypes?.size() ­ 1) {             throw new IllegalArgumentException             ("Row ${row} doesn't have column ${col}.openqa.webdriver import org.size() == 1) {             return cells[0]         }         int size = size()         if (size == 0) {             throw new IllegalArgumentException("The table has no rows")         }         if (row < 0 || row > size ­ 1) {             throw new IllegalArgumentException             ("The table doesn't have row ${row}.size() ­ 1}")         }         throw new IllegalArgumentException("Can't find a cell at ${row}.tagName("a"))     }     public List getCells() {         getRows(). col).get(row)     }     public WebElement getCell(int row.groovy.collect { WebElement row ­>             row.openqa.xpath("tbody/tr[${row + 1}]/td"))         if (columns. int col) {         getCell(row.

if (driver instanceof HtmlUnitDriver) { ((HtmlUnitDriver) driver).get field value (given the field name) . i < Math.get field value (given the field name) Page 11 . So we have implemented a version of them.get number of rows . i++) { UserShowPage userShowPage = userListPage. for (int i = 0.page forward .change sort column . create).setJavascriptEnabled(false).open('/'.email) userListPage = userShowPage. the Grails Plug-in for WebDriver has no built-in classes for the standard Grails pages (i. } } homePage = webdriver. edit.class) userListPage = homePage.e.get number of cols . assertNotNull(userShowPage. 4)..get field names – returns list of strings .username) assertNotNull(userShowPage.Example Test Class The test classes should extend from GroovyTestCase.page backward BaseShowPage (extends AbstractPage) .get field names returns list of strings .min(rowCount. list. class UserTests extends GroovyTestCase { @Rule public WebDriverHelper webdriver = new WebDriverHelper() HomePage homePage UserListPage userListPage @Before public void openHomePage() { WebDriver driver = webdriver. show.open(i). Here are the design notes: AbstractPage Simply extends WebDriverPage AbstractFormPage (extends AbstractPage) .get a cell value . HomePage.goToList() } } } Creating Abstract/Base Classes for Grails Pages Unlike the Grails Plug-in for Selenium.openUserList() @Test public void userShow() { int rowCount = userListPage.getRowCount().driver.set field value (given the field name) o must have some special support for dates and selects BaseListPage (extends AbstractPage) .

o returns the value simply as a string BaseEditPage (extends AbstractFormPage) .offers a submit BaseCreatePage (extends AbstractFormPage) .offers a submit Page 12 .