Wednesday, January 5, 2011

PRACTICUM: Selenium 1.0 Testing Tools: Chapter 4: Using JavaScript

This is the fourth 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.


Two notes: Any and all examples listed can be tried against the book's web site at http://book.theautomatedtester.co.uk/. Make this the Base URL in Selenium IDE for any tests. Also, whenever an Output Log table is shown, that is the format for all Selenium test output and test cases written in Selenese (i.e. it shows the formatting of the Selenium tests in question). Likewise, to help illustrate each example so there is no confusion, I will format the direction steps with regard to the actual command to be created:


(Command: CommandName, Target: TargetName, Value: ValueName)


Chapter 4: Using JavaScript


At the root of Selenium is JavaScript. Therefore, what more logical language to dive into at this stage (well, I'd potentially say Ruby, but I have totally ulterior motives for that, and that's not part of this Practicum series :) ).


JavaScript allows a lot of flexibility for Selenium; the commands utilized most often are actually JavaScript running in the background. For options that don't currently exist, users can "roll-your own" JavaScript within Selenium scripts. Sounds tricky. Is it?


This chapter will cover:
  • Using JavaScript
  • Using variables in your tests with JavaScript
  • Accessing the browser with JavaScript
  • Firing events on elements


Using JavaScript as our Test Language


JavaScript is pretty much the de-facto language of the web when it comes to dynamic web page elements. When Web applications look and feel more like their desktop counterparts, JavaScript has a big hand in that. JavaScript, in addition to providing a lot of interact-able elements on our pages, also allows us the ability to create extensible tests and add functionality if needed.


When A Selenium test step contains JavaScript, the syntax is javascript{ }. This goes into the Target or Value box within Selenium IDE, depending upon the action required. All JavaScript statements must be between the two curly braces. When passing JavaScript into Selenium, the statements are evaluated and the return value will be sent back to your test.


Here's a simple example:


var a, b;
a = 2;
b = 5;
a+b;


This sample would return the Value 7 back to Selenium.


If we had a Web application that would let us enter today's date into a text box, how could we do it? We could make a hard coded value and just enter it, but that would not be very flexible. Wouldn't it be cool to just put in today's date? With JavaScript, we can :).


1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open Target: /chapter4)
3. Use the type command on the locator dateInput to put in the result of the
following JavaScript: javascript{ Date() }. (Command: type, Target: dateInput, Value: javascript{ Date() }
4. Run your script.


Output Log:


[info] Executing: |open | /chapter4 | |
[info] Executing: |type | dateInput | javascript{ Date() } | 


Note that the date field on the page now displays a date:


Wed Jan 05 2011 05:38:06 GMT-0800 (Pacific Standard Time)


That's cool... but that's a bit more of a detailed date than I really need. Fortunately, the date function allows us to use methods to get to the components we really want to use. To do that, we can modify the script so that the javascript value passed back gives us a different format. This next example takes the current date and time and then uses the JavaScript date getHours method to return just the current hour.


1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open Target: /chapter4)
3. Change the dateInput value to just print the current hour (Command: type, Target: dateInput, Value: javascript{d = new Date();d.getHours()} )
4. Run the script.

Output Log:

[info] Executing: |open | /chapter4 | |
[info] Executing: |type | dateInput | javascript{d = new Date();d.getHours()} |


The screen will also show the hour entered, which at the time I tested this, was 5 (yes, I'm an early riser :) ).


So this is cool; we can use the output of a JavaScript method or function call and return it to Selenium to use as an entry value for a field. Nice. But what if I want to use that value in a number of places. Can we do that?


Storing the Result of JavaScript in a Variable


A JavaScript function's return value can be stored as a variable.The book uses the example of a hotel booking: Calculate a date in a week's time, and then on a different page, verify the value is there. At that point, Selenium would use storeEval to store the result and then use it again.


Here's a quick sample of how to use JavaScript to store a variable value and show the value.


1. Open the Selenium IDE.
2. Create a step to storeEval with the JavaScript, javascript{ 10 * 10 }, and
use the variable hundred. (Command: storeEval, Target: javascript{10 * 10}, Value: hundred )
3. Using the echo command, print back to the screen the value stored in variable hundred (Command: echo, Target: ${hundred} ).
4. Run the script.


Output Log:


[info] Executing: |storeEval | javascript{10 * 10} | hundred |
[info] Executing: |echo | ${hundred} | |
[info] echo: 100


The cool thing about this is that you can store variables and you can call them as you need them. Additionally, these variables can also be used when we create assert and verify statements.


Using Selenium Variables with JavaScript


So now we see that we can pass these values back and forth in Selenium to construct tests. These variables
would have been created using one of the store commands, such as storeText. If we want to access that stored value, our tests will need to call the storedVars dictionary. The variable used in the store command is the key for the dictionary. The results are stored in the value object of the key/value pair.


OK, so how does this work?


1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open, Target, /chapter4)
3. Create a step to store the text from the span with the ID bid (Command: storeText, Target: bid, Value: bid).
4. Create a step to add 5 to the variable we used to store the result of the previous step (command: type, Target: nextBid, Value: javascript{+storedVars['bid'] + 5} ).
5. Run the test.


Output Log:


[info] Executing: |open | /chapter4 | |
[info] Executing: |storeText | bid | bid |
[info] Executing: |type | nextBid | javascript{+storedVars['bid'] + 5} |


If you look at the page, the bid box starts with a bid value (and a variable value) of 50. we took the bid value, created a new variable and incremented the original value, incremented it by 5 and typed the new value into the bid text box. The storedVars dictionary was called to get the value of bid. storedVars['bid'] is the same as saying ${bid} if the type command. was being used. David makes the point that the '+' in front of storedVars works to change the value from a string to an integer.


All well and good, but how does this work with a Verify or Assert statement?


JavaScript Within a Verify or Assert


If we have stored a value, can we use that value and make sure it's valued at what we think it's value should be?


1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open, Target, /chapter4).
3. Create a step to store the text in bid (Command: storeText, Target: bid, Value: bid).
4. Create a step to verify that it is equal to 5*10 using the verifyEval command (command: verifyEval, Target: javascript{5*10}, Value: ${bid} )
.
5. Run your test.


Output Log:


[info] Executing: |open | /chapter4 | |
[info] Executing: |storeText | bid | bid |
[info] Executing: |verifyEval | javascript{5*10} | ${bid} |


Accessing the Browser with JavaScript


Picture this... you want to interact with your browser and enter data and verify that the data entered is what should be there, after doing that, you want to interact with the browser, have it work with the data provided, and then check to see that the action was performed, the process completed, and the correct response given. Since Selenium is built from JavaScript, it is capable of interacting with the browser the same way that a user can. Selenium wraps the DOM in a new object when it does this. Therefore, if you want to to access the DOM in your test directly (i.e. not via a Selenium command), you can use a special object called browserbot. If you go with this approach, the test will have to call the window directly and then the window object. From there, you can then do what you want.


The syntax that you will need is:


var window = this.browserbot.getUserWindow();


Accessing the Page With Browserbot

A simple experiment where we concatenate two strings and verify that they equal what we think they should.


1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open, Target, /chapter4).
3. Create a step to call the function concatStrings with two strings as parameters (command: getEval, Target: this.browserbot.getUserWindow().concatStrings("Selenium ","IDE")).
4. Create a step to verify the alert (Command: verifyAlert, Target, Selenium IDE).
5. Run your test.


Output Log:


[info] Executing: |open | /chapter4 | |
[info] Executing: |getEval | this.browserbot.getUserWindow().concatStrings("Selenium ","IDE") | |
[info] Executing: |verifyAlert | Selenium IDE | |


So we can make a call to a page and check to see if something is happening that we want to see, and we don't have to reply on the selenium commands to interact with the browser. So far so good. What if we want to store a value in the page and then call it? Or check to see if a value that's not so readily visible is actually there someplace (like an option listing)?

Well of course we can, otherwise the book wouldn't be teasing us like that ;).


1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open, Target, /chapter4).
3. Create a step to verify that the select with the ID selecttype has four options in it (command: verifyEval, Target: this.browserbot.getUserWindow().document.getElementById("selecttype").options.length).
4. Run your test.




Output Log:


[info] Executing: |open | /chapter4 | |
[info] Executing: |verifyEval | this.browserbot.getUserWindow().document.getElementById("selecttype").options.length | 4 |


Browserbot here gives us the ability of directly interacting with the page and verifying that a particular property is being met (an option list has four options). What this shows is that our tests can use other methods to access page values, not just the ones Selenium defines.


This is great for static details, but what about dynamic pages that use AJAX or some other dynamic method for displaying and producing content? The waitForCondition command has us covered, as in it will wait until the command is true.


According to David, this call exists outside of the Selenium object. To use it, instead of using a local call to this.browserbot, you use selenium.browserbot in the JavaScript instead.


OK, I'm interested :).


1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open, Target: /chapter4).
3. Create a step for waitForCondition to wait till the select is populated.
There is an options property on the select that you can check the length
of to see if it has loaded. (command: waitForCondition, Target: selenium.browserbot.getCurrentWindow().document.getElementById("ajaxLoad").options.length === 4).
4. Run your test.


Output Log:


[info] Executing: |open | /chapter4 | |
[info] Executing: |waitForCondition | selenium.browserbot.getCurrentWindow().document.getElementById("ajaxLoad").options.length === 4 | |


Firing Events


So what do we do if we want to test an action where the mouse is hovering over something or the mouse clicks and  something changes because of that click. The click event is what interests us. How can we see those, test those and confirm those actions?


By using this Selenium API call:

< div id="fireEventDiv" onclick="alert("Alert Thrown")' >

 The call is called fireEvent and it can be used on the following JavaScript events:

  • onFocus
  • onBlur
  • onChange
  • onSubmit
    onMouseOut
  • onMouseOver



1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open, Target: /chapter4).
3. Create a step with fireEvent against the element with the ID hoverOver for the event mousever (command: fireEvent, Target: hoverover Value: mouseover).
4. Create a verifyAlert option to check and see if the MouseOver event works (command: verifyAlert, Target: on MouseOver worked).
5. Run the test.


Output Log:


[info] Executing: |open | /chapter4 | |
[info] Executing: |fireEvent | hoverOver | mouseOver |
[info] Executing: |verifyAlert | on MouseOver worked | |


Let's try that again, only this time let's use the onBlur event (because, hey, why not :)? ).


1. Open the Selenium IDE.
2. Navigate to http://book.theautomatedtester.co.uk/chapter4 (Command: open, Target: /chapter4).
3. Create a step to type something 'blurry'. (command: type, Target: blurry Value: Selenium IDE).
4. Create a step to fireEvent against blurry, the onBlur event alert for what you
typed in the previous step (command: fireEvent, Target: blurry, Value: blur).
5. Create a verifyAlert option to check and see if the blur event works (command: verifyAlert, Target: Selenium IDE).
6. Run the test.


Output Log:


[info] Executing: |open | /chapter4 | |
[info] Executing: |type | blurry | Selenium IDE |
[info] Executing: |fireEvent | blurry | blur |
[info] Executing: |verifyAlert | Selenium IDE | |


onBlur events could be very useful when working with client side validation. This could be especially helpful with forms that do date validations or some other area where entering text needs to be confirmed correct.



JavaScript is pervasive on the web, and it's the backbone of Selenium. using JavaScript in calls allows the user to make more dynamic commands, and in some cases, bypass Selenium's structure entirely. We can store variables, and call on them, we can use JavaScript to create variables, we can use browserbot to change the DOM directly and we can track and fire events and verify that proper action occurs. By giving our tests a more dynamic structure, we help to fight the problem of too-brittle tests, and give them flexibility to test a variety of situations.

2 comments:

Anonymous said...

fantastic

Anonymous said...

good tutorial.... where can I find more usages for the Javascript used in RC?