Google Analytics

Search

To search for specific articles you can use advanced Google features. Go to www.google.com and enter "site:darrellgrainger.blogspot.com" before your search terms, e.g.

site:darrellgrainger.blogspot.com CSS selectors

will search for "CSS selectors" but only on my site.


Showing posts with label AJAX. Show all posts
Showing posts with label AJAX. Show all posts

Wednesday, June 20, 2012

StaleElementException

Most automation tools depend on the concept of the page has finished loading. With AJAX and Web 2.0 this has become a grey area. META tags can refresh the page and Javascript can update the DOM at regular intervals.

For Selenium this means that StaleElementException can occur. StaleElementException occurs if I find an element, the DOM gets updated then I try to interact with the element.

Actions like:
driver.findElement(By.id("foo")).click();
are not atomic. Just because it was all entered on one line, the code generated is no different than:
By fooID = By.id("foo");
WebElement foo = driver.findElement(fooID);
foo.click();
If Javascript updates the page between the findElement call and the click call then I'll get a StaleElementException. It is not uncommon for this to occur on modern web pages. It will not happen consistently however. The timing has to be just right for this bug to occur.

Generally speaking, if you know the page has Javascript which automatically updates the DOM, you should assume a StaleElementException will occur. It might not occur when you are writing the test or running it on your local machine but it will happen. Often it will happen after you have 5000 test cases and haven't touched this code for over a year. Like most developers, if it worked yesterday and stopped working today you'll look at what you changed recently and never find this bug.

So how do I handle it? I use the following click method:
public boolean retryingFindClick(By by) {
        boolean result = false;
        int attempts = 0;
        while(attempts < 2) {
            try {
                driver.findElement(by).click();
                result = true;
                break;
            } catch(StaleElementException e) {
            }
            attempts++;
        }
        return result;
}
This will attempt to find and click the element. If the DOM changes between the find and click, it will try again. The idea is that if it failed and I try again immediately the second attempt will succeed. If the DOM changes are very rapid then this will not work. At that point you need to get development to slow down the DOM change so this works or you need to make a custom solution for that particular project.

The method takes as input a locator for the element you want to click. If it is successful it will return true. Otherwise it returns false. If it makes it past the click call, it will return true. All other failures will return false.

Personally, I would argue this should always work. If the developers are refreshing the page too quickly then it will be overloading the browser on the client machine.

Monday, July 25, 2011

Tools to determine how to use Selenium

Being able to view the source code to a page is helpful when trying to use Selenium. Many of the methods in Selenium require you to locate the element via something related to the source code. For example, if I wanted to click on an element I need to look at the source code. From the user's point of view they might see:

Click Me

But the underlying source code could be:

<span>
    <div>
        <table>
            <tbody>
                <tr><td><strong onclick="javascript:clickme();">Click Me</strong></td></tr>
            </tbody>
        </table>
    </div>
<span>

I might assume there is an anchor and I want to find the <a> element with the text 'Click Me'.  By looking at the source code I can see that I actually want to find a <strong> element and click on it.

Just viewing the source code isn't always sufficient however. AJAX and Web 2.0 means that the source code gets changed in memory after it is loaded. So what is actually available in the DOM (and Selenium) might be different than what you see when viewing the source code.

So how do you see what Selenium sees? A simple solution is to write your Selenium code to get to a point in the program then print out the source code using Selenium. Selenium 1.0 and 2.0 both have methods to return the HTML source code as a string, which you can print.

The problem with this is that you might have to write a program to print the source. Then with the source, find the code you want to click. Then run the program so it clicks the element then prints the new source. Then run the program to click the first element, click the second element and print the source. For n steps this will take O(n^2) run time. Not very efficient.

So how can you view the dynamically changing source? The answer is in browser tools.

If you are using IE7, search for "microsoft dev toolbar". When you install it, it gives you development tools which can view the page in real time. If you are using IE8, the tool is built in. Just press F12 and it will display the development tools.

If you are using Firefox, install the Firebug extension.

If you are using Google Chrome, right click an element and select Inspect to bring up the development tools.

Sunday, February 27, 2011

waitForCondition revisited

In March 2010, I wrote a piece on using waitForCondition with Selenium 1.0, Selenium RC [Java] waitForCondition example.

At the bottom of the article is a line of code that will let you wait for an ajax call to complete. This line of code was written when I was working on a project using scriptaculous.js.

Recently I joined a company using jQuery.js. The code for waiting is the same but the variable you wait for has changed. If you use the code from March 2010, it would fail because the variable does not exist.

Instead you want to use the last line below:

WebDriver browser = new FirefoxDriver();
    Selenium selenium = new WebDriverBackedSelenium(browser, baseURL);
    browser.get(baseURL);

    selenium.waitForCondition("selenium.browserbot.getCurrentWindow().jQuery.isReady == true", "30000");

You might wonder how I figured this out. The easiest way is to ask the developer. If that is not an option, Firebug on Firefox or the Inspect feature of Chrome will help you out. On Chrome I would right click on the web page and select Inspect Element. The Developer Tools will appear. Select the Scripts section. On the right will be Scope Variables with a pause button at the top of the frame. Clicking the pause button will display the Scope Variables. You then want to look at the variables and see if something jumps out at you.

The other approach is look at the select menu at the top of the left frame. This will list the different javascript files. When I looked at this list I saw the jquery.js script. I could then look up the APIs for jquery.js and figure things out.

Tuesday, March 16, 2010

Selenium RC [Java] waitForCondition example

I've seen a few people post questions about using the Selenium waitForCondition method.

If you read the documentation for the waitForCondition method (in either Selenium or DefaultSelenium) you will see a mention of selenium.browserbot.getCurrentWindow(). This is key to the wait for condition.

When you start a test case there are two windows which open up. The first window has the Selenium Command History and ability to display the log. I call this the Selenium window. The next window which opens is the window for the application under test or AUT window.

The waitForCondition method has two parameters. The second parameter is the easiest to understand. We don't want the test to wait for ever. If we don't get a response by a certain time we can assume the test failed. The second parameter is the timeout period in milliseconds. So if the condition should occur within 20 seconds, the second parameter is "20000".

The second parameter is the key to the waiting. It is the condition we are waiting for. There are two windows open, Selenium and AUT. The first parameter to waitForCondition is a javascript expression. It will normally be tested on the Selenium window. If you want to evaluate some javascript in the AUT window you need to use: selenium.browserbot.getCurrentWindow(). For example, Let's say this is the AUT window:



<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
// this variable is the key. When it is false the loading is done
var loading=true;

function loadingDone() {






// hidden the GIF
document.getElementById("loading").style.visibility="hidden"; // set the flag to indicate we are done loading
loading=false;
}
// this sets a timer for ten seconds to simulate something
// loading for ten seconds. After ten seconds this will call
// the loadingDone() function.
var seconds=10;
var t=setTimeout("loadingDone()", seconds * 1000);
loading=true;

</script>
<!-- the moment the page loads this will create -->
<!-- a spinning GIF to signal something loading -->
<img id="loading" src="http://www.oshawa.ca/images/loading.gif" style="visibility:visible"/>
</body>
</html>


What this page does is simulate something loading. After 10 seconds it will hide the spinning GIF and set the loading variable to false. So the condition we are waiting for is when loading == false.

This means from waitForCondition we want to look at the variable "selenium.browserbot.getCurrentWindow().loading == false". In other words, the full statement is going to be:

selenium.waitForCondition("selenium.browserbot.getCurrentWindow().loading == false", "20000");

It is important to note that the selenium in selenium.waitForCondition is a Java object in your test code and the selenium in selenium.browserbot is a javascript object running in the web browser.

Let's say we take this HTML and save it to the file /Users/darrell/workspace/LearningSelenium/waitForCondition.html then we can create the following Selenium RC test case:



package com.example.tests;

import org.openqa.selenium.server.RemoteControlConfiguration;
import org.openqa.selenium.server.SeleniumServer;
import com.thoughtworks.selenium.DefaultSelenium;
import com.thoughtworks.selenium.SeleneseTestCase;

public class TestingSelenium extends SeleneseTestCase {

 SeleniumServer ss;
 RemoteControlConfiguration rcc = new RemoteControlConfiguration();

 public void setUp() throws Exception {
  int ssPort = 4444;
  rcc.setPort(ssPort);
  rcc.setTimeoutInSeconds(1200);
  ss = new SeleniumServer(rcc);
  ss.start();
  selenium = new DefaultSelenium("localhost", ssPort, "*safari", "file://");
  selenium.start();
 }

 public void testStub() throws Exception {
  long start, stop;
  selenium.open("/Users/darrell/workspace/LearningSelenium/waitForCondition.html");
  start = System.currentTimeMillis();
  selenium.waitForCondition("selenium.browserbot.getCurrentWindow().loading == false", "300000");
  stop = System.currentTimeMillis();
  System.err.println("Elapsed time: " + ((stop - start) / 1000.0) + " seconds");
 }

 public void tearDown() throws Exception {
  super.tearDown();
  ss.stop();
 }
}


and that is one example of waitForCondition. You will have to know what condition to wait for in your application. If you are using AJAX you might find there is an AJAX object on your application. In it will be a activeRequestCount. When the AJAX.activeRequestCount goes to zero, all the AJAX calls are done. So you can wait for an AJAX call to complete with:


selenium.waitForCondition("selenium.browserbot.getCurrentWindow().AJAX.activeRequestCount == 0", "30000");

Wednesday, August 8, 2007

AJAX

It has been a while since I have posted. I've worked on a number of things but I think the 'hot' topic right now would be testing AJAX. AJAX stands for Asynchronous Javascript And XML. See Wikipedia's page on AJAX for more information.

A few years back a web site might be set up so you have two frames (left and right). The left frame has a tree view. When you select something in the tree view the web browser sends an HTTP Request to the web server. The web server response with an HTTP Response to the web browser. The browser then displays it to the right frame.

Basically, every time you click the appropriate link, the browser sends a synchronous HTTP Request, the user waits for the response, then it display.

Along comes AJAX. Now the tree view is javascript. When you select something in the tree view, AJAX sends an HTTP Request directly to the web server. The web browser is unaware of the request and therefore does not wait for the response. You can continue using the browser while AJAX, asynchronously, waits for the response.

The problem with testing AJAX is that most test software detects when the web browser does an HTTP Request and waits for the HTTP Response. Because AJAX handles the request and response, the test tools are unaware there is a need to wait. If the selection in the tree view took 3 seconds, the test script will click the tree view and nanoseconds later expect the results to be in the right frame.

Solution #1: I can put a sleep for 3 seconds. Problem is, network conditions change. It might be next time it takes 5 seconds or 7 seconds. We could brute force it and make the sleep 1 hour. But if it sleeps for 1 hour on each click and there are more than two dozen clicks, it will take over a day for even a simple test case. NOT A GOOD SOLUTION.

Solution #2: If my test suite is written in javascript and running in the same authentication domain as the website I'm testing (e.g. Selenium) then I could write my AJAX so it sets a flag when it does the HTTP Request and clears the flag when it gets the HTTP Response. Now the test code can wait for the flag to get set then wait again for the flag to be cleared before it assumes the right frame has the results in it. This is a good solution but it requires you to modify the application under test (AUT).

Solution #3: Create a man-in-the-middle setup. The way that man-in-the-middle works is original from a phishing (fraud) scheme. You have probably seen it. You get an email from 'your bank' telling you to log in and fix a problem with your account. The link says http://mybank.com but the actual link is to http://mybank_com.evil_doers.com. The evil_doers.com website will receive the HTTP Request from your browser, look over the information you are sending then pass it on to the real mybank.com. When mybank.com receives it, it will log you in and send an HTTP Response back to evil_doers.com. The evil_doers.com will examine the response, log it and send it back to you.

It is like putting a wire tap on your telephone. We can use this for good. I have host1.com running the web browser. I have host2.com running man-in-the-middle software and it will forward things to testsite.com. On host1.com I would normally go to http://testsite.com/my_fabulous_app/index.jsp. Now I go to http://host2.com/my_fabulous_app/index.jsp. The man-in-the-middle software will be my test software.

Realistically, I could actually run the web browser and the test software on the same machine. I'd have less security issues if I did that and the URL would become http://localhost/my_fabulous_app/index.jsp.