Monday, August 10, 2009

Testing Ajax Applications With Selenium

On a rails application I was building last year, we had chosen to use prototype for some ajax features. We were using selenium to test the application. Previously it was common that any time we interacted with the page via a click, or a select, we'd follow it up with a wait_for_page_to_load command. This would ensure that selenium (or rather the driver we were using), would only send the next command to selenium once the page we were interested in was completely loaded.

On an Ajax application (or on an Ajax feature of a web site), this never happens. Small sections of the page might update. It's still necessary to wait until this process is complete before interacting with sections of the page where content or controls are yet to be rendered. A simple sleep or wait is a bad idea. These ajax calls rarely take exactly the same time to perform an operation every time. If the wait period is too short, you'll have random test failures since a control you want to interact with is missing. If the period is too long, your tests take too long since you end up waiting, very often, far longer than you need to.

The recommended approach is to use one of the selenium wait_for's. There's a wait_for_element_present I believe, which for some reason I cannot find in the rc driver at the moment. Anyway, that function waits till a particular element shows up on the page. The other option is wait_for_condition, which takes a javascript function and waits till it evaluates as true. See here.

The annoying thing about this approach though, was that there's a big advantage in having a generic "wait_for" statement that can be used anywhere, that just waits till any Ajax calls are done. You could write custom wait_fors each time that are relevant to the particular test you are writing. But I see a generic solution being useful in a variety of conditions. For example
  • you have a certain element where all that changes is content. This allows you to differentiate between a failure because a call was not completed and a failure because of an actual bug.
  • you have certain wizard style flows which you have to navigate through in multiple tests and you'd rather not worry about exactly what must have changed after every step.
  • You fire multiple ajax calls on some interaction and you need to wait for all of them to complete ideally.
Since we were using prototype for our javascript and wait_for_condition accepted a javascript snippet, it made sense to see if we could use this to our advantage. Digging around in the selenium source code we discovered that selenium.browserbot.getCurrentWindow() gave you access to the test window and all the globals. Further it turned out that prototype actually has a global variable under the Ajax namespace activeRequestCount. So all we had to do was wait for the condition that this variable was zero. This solution worked beautifully.
wait_for_condition("selenium.browserbot.getCurrentWindow().Ajax.activeRequestCount == 0",timeout)

There's another solution that I know of. Twist apparently supports a proxy injection mode, where all your requests are routed through a proxy. This is interrogated by the test runner which is now always aware that whenever there is an active request, ajax or otherwise. This allows implicit waits. This is probably the cleanest solution that I know about. I've never actually used it though and so if you aren't using twist, if proxy injection mode isn't possible for some technical reason, or if this is likely to make the tests slower then the previous solution is a better idea.

In a follow up post Ill actually use an example and demonstrate how to use this wait_for_condition to write a generic ajax wait with prototype or jquery or even a custom rolled javascript framework.

Thanks to Reggino, this is the required piece of code for Jquery
`selenium.browserbot.getUserWindow().$.active == 0`

29 comments:

  1. making all ajax calls sync in test is another approach, an advantage of this approach is that all javascript exceptions are catchable from your testing code.

    ReplyDelete
  2. http://www.markhneedham.com/blog/2009/05/14/selenium-waiting-for-jquery-ajax-calls/

    and

    http://www.luning.name/logs/39397928.html

    are some similar discussions.

    ReplyDelete
  3. "wait_for_element_present" (or waitForElementPresent in Java) is, ahem, present, in the core/ide API, but not in RC's.

    In RC, the idiom is to call "is_element_present" in a loop. There's been some debate for adding "wait_for_element_present" directly in the RC API, though, for the specific reason that that the API differences cause confusion. :-)

    Excellent post, and thank you for writing this up. Getting proper waits/blocking in place for ajax apps is a frequently asked question among Selenium users.

    - Jason Huggins
    creator: selenium
    twitter: @jhuggins

    ReplyDelete
  4. thanks luning. I was planning to cover jquery too, but looks like atleast in that case the solution is known :)

    ReplyDelete
  5. thanks Jason
    I was wondering if it was possible for to use user extensions to actually extend the xmlhttp request object. To come up with a generic solution that works across ajax frameworks. Without using the proxy injection mode. I don't know if that makes sense just yet though =p

    ReplyDelete
  6. Thnx for the info. A small sidenote: I had to use

    selenium.browserbot.getUserWindow().$.active == 0

    to get stuff working for Jquery / Selenium IDE 1.0.7 Firefox plugin

    ReplyDelete
  7. I can not see the data in an unordered list. I tried using XPath expression and identifiers, but selenium can not find them. Any advice would be greatly appreciated. Thanks!

    Dubturbo

    ReplyDelete
  8. Vishnu,

    Just a small correction. Twist includes Selenium 0.9.2 as a driver. ( We do recommend Sahi for web testing though ).

    We've made enhancements to Selenium to take care of these things ( including , but not limited to , waitForPageToLoad and waitForAjaxRequest API's )

    We also have implicit wait support.

    ReplyDelete
  9. Hey Manish, this thing is more than a year old =p. Btw what modifications have you made to waitForAjaxRequest? How does it work? And is it independent of any js frameworks?

    ReplyDelete
  10. I must confess that in the past all my AJAX pages have been tested with pause, not only because of the limitations of selenium, but also because it gave me a way to see what happens when time standby server.

    ReplyDelete
  11. Hi,

    I am struggling to fix this problem i have selenium IDE generated code
    //div[@id='ext-gen25']/div[10]/table/tbody/tr/td[2]
    In this ext-gen25 is dynamically changed to ext-gen29, ext-gen45 for each run. How can i use XPATH to locate this each time correctly.

    Thanks,
    Basham

    ReplyDelete
  12. what you need are xpath functions
    http://www.w3schools.com/xpath/xpath_functions.asp

    so instead of testing id equality, test some condition on id

    ReplyDelete
  13. Well Said, you have furnished the right information that will be useful to anyone at all time. Thanks for sharing your Ideas.
    Salesforce Training in Chennai | Salesforce Training Institute in Chennai

    ReplyDelete
  14. Great efforts put it to find the list of articles useful for Selenium, Definitely will share the same to other forums.
    We are also one of the best sources to learn Selnium -Selenium training in Chennai |Best Selenium training institute in Chennai

    ReplyDelete
  15. Thank you a lot for providing individuals with a very spectacular possibility to read critical reviews from this site.

    selenium training in bangalore|

    ReplyDelete
  16. Great post!
    Thanks for sharing this list!
    It helps me a lot finding a relevant blog in my niche!
    Java Training in Chennai
    Java Training in Coimbatore
    Java Training in Bangalore

    ReplyDelete
  17. As a Microsoft program, Visio not only uses the same ribbon-style layout as Word, Excel, and PowerPoint, but it’s fully integrated with these programs as well.Click Here

    ReplyDelete
  18. AnyToISO Professional 3.9.8 Crack is an ISO file format converter software. It's straightforward software, and it works very well. AnytoISO Download Full Version Free Mac

    ReplyDelete
  19. Thanks for sharing this information. I really like your blog post. Enhance your English language skills with top-notch online tuition in Bahrain. Ziyyara Edutech’s experienced English language tutor in Bahrain offers the best personalized tutors.
    For more info visit English language tutor in bahrain

    ReplyDelete
  20. Awesome Blog!!! Thanks for it, it is more useful for us. Are you a Class 12 student grappling with doubts in accountancy subjects like Profit and Loss, Journal Entry, Financial Ratios and more.
    Book A Free Demo Today visit Private tuition classes for class 12

    ReplyDelete