Académique Documents
Professionnel Documents
Culture Documents
Created 12/15/10
Updated 02/24/11, Updated 04/16/11, Updated 05/01/11, Updated 06/17/11, Updated 09/23/11, Updated 02/07/12,
Updated 02/15/12, Updated 02/27/12, Updated 03/23/12, Updated 05/12/13, Updated 10/19/14, Updated 12/30/14
http://stackoverflow.com/questions/6829465/getting-element-in-webdriver-not-by-xpath
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 in 2010 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 isnt 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.44.0, released October 2014. 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 APIs but moving toward Selenium 2.
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.
Page 1
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.
assertThat(driver.getTitle(), containsString("cats"));
}
In this example, we create a WebDriver, then get a page, then find an element and perform operations on it.
Finally, we check that the page title has changed to contain the expected value.
Except for the WebElement class, you could perform similar steps with Selenium 1, however, there is much more
flexibility because of the various implementations of WebDriver (for different browsers), and the operations on the
WebElement provide a good abstraction. Both of these class structures are discussed in the sections below.
Driver Implementations
WebDriver class is available in several implementations. The most general is RemoteWebDriver, which allows for
the programmatic specification of the test browser and its runtime. See
http://code.google.com/p/selenium/wiki/RemoteWebDriver. Here is the API:
Page 2
This enables you to set up Firefox or other runtime environments, including a headless front-end. The various
subclasses of RemoteWebDriver, such as FirefoxDriver are simply preconfigured versions of RemoteWebDriver.
The headless front-end typically uses HtmlUnitDriver (see http://en.wikipedia.org/wiki/HtmlUnit). Please note
some of the restrictions on use of JavaScript in this (it is simply running Rhino as a JavaScript engine, which is not
entirely the same as current production browsers. See http://code.google.com/p/selenium/wiki/HtmlUnitDriver).
Version Compatibility
Apparently there is an update required to the WebDriver library for most updates to browsers such as Firefox. See
http://stackoverflow.com/questions/7733824/selenium-webdriver-firefox-7-0-1-incompatibility for a discussion.
Hence, we found that we often could not update to new browsers, such as Firefox 10 during early 2012. Instead, we
are using Firefox 4.0, which dates back to early 2011, but matches the 2.8.0 version of the WebDriver library.
Page 3
Plus, you can search for an element within a given elements scope: findElementBy(By by) and findElementsBy(By
by).
There are special operations to handle select elements on your web page:
Page 4
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, which also offers position information (size, click
at location, etc.).
Handling Waits
See http://seleniumhq.org/docs/04_webdriver_advanced.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.
The worst case of this is Thread.sleep(), which sets the condition to an exact time period to wait. There are some
convenience methods provided that help you write code that will wait only as long as required. WebDriverWait in
combination with ExpectedCondition is one way this can be accomplished.
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. The default setting is 0. Once set, the implicit wait is set for the life
of the WebDriver object instance.
These waits are needed for clicks on the page elements, as the WebDriver itself doesnt wait (since the click may be
an ajax update)
Page 5
public void open(String url) {
driver.get(url);
}
You could also change the constructor to throw an exception if a required element is not present. This is similar to
the verifyPage() behavior in other page models.
A further refinement is that you can specify the lookup of the WebEements by annotation, rather than performing
init yourself, if the page is created by the PageFactorys initElements method (see below). For example:
@FindBy(id="q")
private WebElement searchField;
@FindBy(name="btnG")
private WebElement searchButton;
Page 6
public void typeSearchTerm(String searchTerm) {
searchField.sendKeys(searchTerm);
}
By default, each element on the page is looked up each and every time a method is called upon it. To change this
behavior, simply annotate the field with the {@link CacheLookup}.
In the above example, the PageFactory will create the class (running the constructor), then fill out each of the
elements. So the constructor can carry out a set of direct element lookups as part of verify() sequence, then have the
PageFactory carry out additional setup and checking.
Question: there is a NoSuchElementException. When is this thrown? Answer: when findElement fails, either in a
direct call or in a call from the PageFactory.
The PageFactory is your friend, as it handles the initialization of those objects for which the findElement processing
is useful. This includes all pages with specific named, idd, or classed elements.
@Before
public void openXXXPage() {
WebDriver driver = webdriver.driver;
if (driver instanceof HtmlUnitDriver) {
((HtmlUnitDriver) driver).setJavascriptEnabled(false);
}
Page 7
xxxPage = webdriver.open('/', XXXPage.class)
}
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. The tests then just interact with these page objects, making assertions and
navigating around your application. 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.
Essentially, using a subclass of WebDriverPage is like always instantiating your page with the PageFactory at the
Java level. Reviewing the source of WebDriverPage showed us how it organizes the checks for title, the checks for
elements, etc.
However, you could define a constructor for the page, and carry out specific findElement calls to create
WebElements. For verification, you can override verify(boolean newPage), and call the superclass method then
code your verification steps.
For instance, LinkElement offers the clickTo() method which takes a class object. The clickTo method will click the
link, a return an instance of the specified page class (which would be for the destination of the link).
For documentation on the various specific subclasses of web element, see https://bitbucket.org/refactor/grails-
webdriver/src/9fad4997c4cc/test/unit/pages/test/TestOnePage.groovy. The full list of such classes is shown below:
Page 8
Most of these are subclasses of WebDriverPageElement.
If you have a table with known columns, you can use a TableElement to access the data inside. In this example, the
BookRow represents a single <tr> within the table. Here is the test:
@Test
publicvoidtestList(){
ListBooksPagelistBooksPage=webdriver.open('/books/list',ListBooksPage)
assertEquals(["Id","Title","Author","Type","Used"],
listBooksPage.books.columnHeaders)
assertEquals(1,listBooksPage.books.size())
assertEquals("Cryptonomicon",listBooksPage.books.rows[0].title)
}
classListBooksPageextendsWebDriverPage{
staticexpectedTitle="BookList"
TableElement<BookRow>books
staticelements={
books(By.xpath("//table"))
}
}
classBookRowextendsWebDriverPageElement{
Stringid
Stringtitle
Stringauthor
Stringtype
Stringused
staticelements={
id(By.xpath("td[1]"))
title(By.xpath("td[2]"))
author(By.xpath("td[3]"))
type(By.xpath("td[4]"))
used(By.xpath("td[5]"))
}
Page 9
}
importorg.openqa.selenium.By
importorg.openqa.selenium.WebElement
publicclassTableElement<T>extendsWebDriverPageElement{
List<String>columnHeaders
List<T>rows
staticelements={
columnHeaders(By.xpath("thead/tr/th"))
rows(find:By.xpath("tbody/tr"),listElement:
{config.tableRowElement?:config.genericTypes?.get(0)?:WebElement})
}
publicintsize(){
returngetRowCount()
}
publicintgetRowCount(){
rows.size()
}
publicTgetAt(introw){
rows.get(row)
}
publicWebElementgetCell(introw,intcol){
defcells=webElement.findElements(By.xpath("tbody/tr[${row+1}]/td[${col+1}]"))
if(cells.size()==1){
returncells[0]
}
intsize=size()
if(size==0){
thrownewIllegalArgumentException("Thetablehasnorows")
}
if(row<0||row>size1){
thrownewIllegalArgumentException
("Thetabledoesn'thaverow${row}.Validrowsare0..${size1}")
}
Listcolumns=webElement.findElements(By.xpath("tbody/tr[${row+1}]/td"))
if(columns.size()==0){
thrownewIllegalArgumentException("Row${row}hasnocolumns")
}
if(col<0||col>columns.size()1){
thrownewIllegalArgumentException
("Row${row}doesn'thavecolumn${col}.Validcolumnsare0..${columns.size()1}")
}
thrownewIllegalArgumentException("Can'tfindacellat${row},${col}inthetable")
}
publicWebElementgetLink(introw,intcol){
getCell(row,col).findElement(By.tagName("a"))
}
publicListgetCells(){
getRows().collect{WebElementrow>
row.findElements(By.xpath("td"))
}
}
}
Page 10
Example Test Class
The test classes should extend from GroovyTestCase.
HomePage homePage
UserListPage userListPage
@Before
public void openHomePage() {
WebDriver driver = webdriver.driver;
if (driver instanceof HtmlUnitDriver) {
((HtmlUnitDriver) driver).setJavascriptEnabled(false);
}
@Test
public void userShow() {
int rowCount = userListPage.getRowCount();
assertNotNull(userShowPage.username)
assertNotNull(userShowPage.email)
userListPage = userShowPage.goToList()
}
}
}
AbstractPage
Simply extends WebDriverPage
Page 11
o returns the value simply as a string
Page 12