Tuesday, January 4, 2011

PRACTICUM: Selenium 1.0 Testing Tools: Chapter 2: Locators

This is the second entry for the TESTHEAD PRACTICUM review of PACKT Publishing's book Selenium 1.0 Testing Tools Beginners Guide by David Burns. The emphasis on this type of review is the actual exercises and the practical applications of what we learn. Rather than reprint out the full listing of the exercises in the book (and there are a bunch of them, as that is the format of this particular title), much of these chapters are going to be a summary of the content, some walk-through of application, and my take on what I see and do.


Selenium 1.0 Testing Tools is full of examples, "try it yourself" instructions and explanations of what happened. The book’s cover states “Learn by doing: less theory, more results”. The focus will be less on the concepts and more on the actual implementation and methods suggested.


Chapter 2: Locators


Locators allow the tester to find elements on pages so that they can be used in tests. With HTML, it is considered good practice to make sure that each element you interact with has an id and a name attribute. But what happens when the content is generated dynamically. Is it possible to always guarantee a name and an ID? What should we do if we can't.

Chapter 2 looks at:


  • Locating elements by ID
  • Locating elements by name
  • Locating elements by link
  • Locating elements by XPath
  • Locating elements by CSS


Prerequisites for this Chapter:


In addition to having Selenium IDE up and running, you will also need to have the following applications installed and running.



  • Firebug: https://addons.mozilla.org/en-US/firefox/addon/1843: Firebug has become the de facto tool for web developers, as it allows developers to find elements on the page by using the find functionality. It has a JavaScript REPL. REPL stands for Read-Eval-Print-Loop, which is an interactive shell that allows you to run JavaScript without having to create an entire page.








  • IE Developer Tools: This is built into IE7 and IE8, which we can launch by pressing F12. It also has a number of features that Firebug has.




  • Google Chrome Developer Tools: This, like IE, is built into the browser and will also allow you to find the elements on the page and be able to work out its XPath.


Once you have determined which tool you will use to help locate the items in question, you can drop them into Selenium IDE and test them. IF you put a value in the Target Text box, and click on the Find button, if it can locate the item a green box will appear around the item in question (this helps considerably in making sure that the value you are providing is indeed the one that the page and Selenium IDE can work with).


Locating Elements By ID

This exercise will require Firebug to complete, so "fire it up" by clicking on the Firebug Icon. Also, open the home page for the the book at http://book.theautomatedtester.co.uk/ and select "Chapter2". It's obvious after you do it that that is what is meant, but since it's not listed as such, you may be scratching your head for a minute wondering what you are supposed to be looking at.


1. Click on the Firebug icon.
2. Click on the icon to the left-hand side of Console (looks like a text box with an arrow pointing in it).
3. Move the mouse around the screen. Hover over items on the screen.
4. Move your mouse and hover over different elements. Firebug highlights each of them in turn and shows the name and HTML associated with each item.


So with this, we have a tool that will help us find the elements and their particular attributes that we can enter into the Target and create values for them. Cool, huh?



Elements by ID in Selenium IDE 

Elements often have IDs that are used to locate them. In the Target text box, this would look like id=element. Follow the next example to see how it would work:


1. Open Selenium.

2. Open Firebug (notice that, as you are selecting items in firebug, no input is made in the Selenium IDE tool itself, i.e. none of these actions are being recorded.

3. Find an element that you want to interact with and in the Target box place its ID attribute value. For example, use but2 as seen in the previous screenshot against http://book.theautomatedtester.co.uk/chapter2.

4. Type the command click into the Command select-box.

5. Run your script.


Output will look something like this in the Log Section:

[info] Executing: open /chapter2

[info] Executing:  click but2


The cool think to be aware of is that the element itself was found and the action was performed on the element, not its location or its geometry on the page. If this item were to be moved to another spot on the page, the action would still be executed because it would still be targeting the item by its ID rather than it's position. 

Finding Elements by Name


Not all elements have name attributes to target. If there is a named element, though, we could reference them like this:


1. Open Selenium.
2. Open Firebug.
http://book.theautomatedtester.co.uk/chapter2.


You can also add filters to the name in case there are multiple elements with the same name. In the example, there are two buttons with verifybutton as its name, but one of them has the value of "chocolate". By defining the value, the script targets the correct item.


Finding Elements by Link Text


Following links is simple w/ Selenium, you just define the link type and click the link that's been defined. The step omits the click option, but allows the user to see that selecting the Target text box and entering in the command link=Index, will access the link on the page that brings the user to the main page.

1. To specify that you want to follow a link, you would use the target link=link.
2. Verify a link by using "verifyElementPresent".
3. On the chapter2 page, there is a link to the index page of the site.
4. Enter "Link=Index" in the Target text box.
5. Chick Find and see the link highlighted by a green flashing box.
6. Run the script.


You will see an output that looks something like this:

[info] Executing: open /chapter2
[info] Executing: click verifybutton
[info] Executing: click //input[@name='verifybutton' and @value='chocolate']
[info] Executing: verifyElementPresent link=Index


Finding Elements by Accessing the DOM Through JavaScript


So what do we do when we have dynamically generated content, say through AJAX. In this case, we need to see if the content even exists before we can act. In this case, the locator will need some JavaScript to see if the element in question is even there.

The waitForCondition command allows us to do this, so depending on how the pages are coded, it may be a simple or complicated method to get the element, but both approaches use the same Command. In this case, we can create a function that will actually create the desired data, and then return the value expected.


The book uses an example of a regular expression:

function searchLinks (){
   var links = document.links,
   h = new RegExp(/http:\/\//);
   for (link in links){
     if (link.toString().match(h)) { return link; }
   }
}
searchLinks();

If you are doing a lot of dynamic interaction and need to confirm those elements, then creating JavaScript functions to create the necessary data could prove to be very helpful.



Finding Elements by XPath


What if we have a page that is created that uses a key from the database as the element ID. If something is edited and stored back in the database, we can find the record again and update it? XPath allows the user to query the DOM as though it were an XML document. XPath allows for complex queries to find elements on pages that may not be accessible in other ways.


Let's start by creating a basic XPath. We are going to look for an input button.

1. Open the Selenium IDE.
2. Enter click into the Command select box.
3. Enter xpath=//input into the Target box.
4. Click Find. Notice that the button "Button With ID" flashes.
5. Run the test. Notice the output in the log:

[info] Executing:  click  xpath=//input


Also note that the user does not have to be in record mode to make these test steps, they can be entered in manually by selecting a Command, the entering in a Target, and if desired, entering a Value. Clicking below that entry creates the next line entry for the table, and the user can enter the next set of details for the next test step (the book doesn't point this out explicitly, but it becomes obvious with repeated practice).


Calling an Element with Direct XPath


Using // as the start of the XPath will parse the entire DOM until it finds the element that you want to find, which is excessive and in certain situations may render a poor performing test. If you know an element will be in a certain place, call it directly. You can use a single /, but this requires the first part of the query be "HTML".


Here's an example:

1. Open Selenium IDE.
2. In the Target, enter "xpath=/html/body/div[2]/div[3]/input".
3. Click on the Find button.


This action will find the same element, but it will find it faster than by using the full query option (granted, faster in this case is a fraction of a second, but add up tens of thousands of tests and that fraction could be significant). If the UI changes, however, the test may fail if the element is moved to a different location on the page.


Using XPath to Find the nth Element of a Type

Many times there will be elements that are created that do not have a unique name of ID.

In these cases, when a query is run against the DOM, a number of similar elements are returned to Selenium that matches the query. these are then stored in an array. All elements needs to be "sibling nodes" of the first element returned. If they are not sibling nodes you will receive a fail saying that the element could not be found.


Using Element Attributes in XPath Queries

Often elements are the same except for one or two attributes. Attributes can be added to queries. The format will be something like "xpath=//element[@attribute='attribute value']".

If two div elements are on the page and the only difference is the class attribute, the query would look like:


xpath=//div[@class='classname'].


Doing a Partial Match on Attribute Content


Often, there's is no to create a static ID for elements on the page. The element could be loaded asynchronously via AJAX or uses a key value in a database. Perhaps only part of the ID is dynamic. In this case, making a partial match may give us what we need.


The words "contains" and "starts-with" allows the user to search for attributes and make a partial match. The "text()" element also allows the user to create a search for the same string. The book uses the example where the text to look for is "This element has an ID that changes every time the page is loaded".

The following approached can help find this string or the element by using queries like:

//div[contains(@id,'time_')]
//div[starts-with(@id,'time_')]
//div[contains(text(),'element has an ID')]
//div[text()='This element has an ID that changes every time the page is loaded']


Using XPath Axis to Find Elements


XPath is used generally when the element is not accessible by normal means. The book uses an example where a table cell has specific text and then works backwards to click an edit button.


In the first example, we are going to find a button and then find its sibling. The query that is created starts with finding the first element of the query:


 //input[@value='Button with ID']


Place that into the Selenium IDE Target box and see which element it highlights. There's another button below the highlighted one, and that the element we want to work with. to get access to this item, we can use "following-sibling" like this:


//input[@value='Button with ID']/following-sibling::input[@value='Sibling Button']


The Xpath values can be accessed using a number of different values such as:



ancestor: Selects all the ancestors (parent, grandparent, and so on) of the element
descendant: Selects all the descendants (children, grandchildren, and so on) of the element
following: Selects all elements that follow the closing tab of the current element
following-sibling: Selects all the siblings after the current element
parent: Selects the parent of the current element
preceding: Selects all elements that are before the current element
preceding-sibling: Selects all of the siblings before the current element


CSS Selectors


One way that XPath selectors can give tests flexibility is to help fine elements on the page. Finding Elements by XPath can be a lengthy and involved process though. Another way is to use CSS selectors. Selenium supports CSS 1.0, CSS 2.0, and CSS 3.0 selectors.


Using Sibling Nodes to Find Elements


CSS selector syntax uses a '+' between DOM nodes. This checks the direct next node to see if it matches, until it finds the element.  



css=input#but1 + br + input


Place the above string in the target text box and click "Find" to see the effect.


Using CSS Class Attributes in CSS Selectors


To find the div value with the class centerdiv, you can enter the following query:

css=div.centerdiv

Place this in the Target textbox and then click "Find".


Using Element IDs in CSS Selectors


There are times we need to find an element that is next to another element w/ a known ID. To find an element by ID in a CSS selector, place a '#' in front of the ID of the element. To find the div with the ID of divinthecenter use:

css=div#divinthecenter


Finding Elements by Attributes


Lets look for the button that has the value 'chocolate'. The syntax for looking at the attribute is

node[attribute='value']


For the button, we would use


input[value='chocolate']


to use w/ the Selenium IDE, enter


css=input[value='chocolate']


into the Target text box and click "Find". To find an element according to its href, you can use


a[href='path']


On the main index page, you can enter the link to Chapter 2 like this:


css=a[href='/chapter2']


Chaining of attributes is also allowed to ensure that your test is accessing a specific item. The syntax will be

css=node[attr1='value1'][attr2='value2']


Using the Chapter two page, try this value:


css=input[id='but1'][value='Button with ID']


This will find the button with the value Button with ID. You can use as many chains as you need to isolate the value you are after.


Partial Matches on Attributes w/ CSS


Just as with XPath, dynamically generated values can be hard to pin down, but it's possible if using partial match options. To do that, see the following examples:

^=: Finds the item starting with the value passed in. This is the equivalent to the XPath starts-with.
$=: Finds the item ending with the value passed in. This is the equivalent to the XPath ends-with.
*=: Finds the item that matches the attribute, which has the value that partially matches. This is equivalent to XPath contains.


We looked at an element with a common text value but a dynamic ID. CSS selector statements we could use are:

div[id^='time_']
div[id*='time_']


Examples (enter into the Target text box and click "Find"):


css=div[id^='time_']
css= div[id*='time_']


Finding the nth Element with CSS

In the XPath examples, the second input after the div with the class leftdiv was used. To do this with CSS, we need to use pseudo classes. Pseudo classes are used to add special effects to selectors. the following statements will find the same item (Enter both into the Target test box and click "find" to see).

Xpath: xpath=//div[@class='leftdiv']/input[2]
CSS: css=div#divinthecenter *:nth-child(2)


The down side to this approach is that (as of the book writing) Selenium doesn't support nth-of-type pseudo class. This is why the selector is using the wild card * and then finding the nth-child from the starting div. If any other node was in the way, this statement would fail.



Finding an Element by Its Inner Text using CSS


In the XPath section we used 'text()' function to see the text it had. The following contrasts how to make the same call using CSS:

Xpath: xpath=//div[contains(text(),'element has a ID')]
CSS: css=div:contains('element has a ID')


Note: the CSS selector is not as granular as the XPath call because it may look at the descendants for the text.


Searching for elements and making sure that you have the correct one for your test can be simple or challenging depending on what you are looking for. static values will be the easiest to find using tools like firebug and defining the values directly. With AJAX or other dynamically driven content, the approach may be a little more challenging. Xpath and CSS calls allow the user to isolate and focus on the items needed, even if they are dynamically generated. the steps may be more involved and require more in-depth queries, but with a little focus and imagination, most items a page can be displayed can be accessed.

No comments: