Tuesday, May 17, 2011

Migrating from Selenium 1.0 to Selenium 2.0

1. The new Selenium 2.0 should run much faster (2-4x?) , for this reason in that it incorporates WebDriver (Google has a good writeup explaining the differences http://google-opensource.blogspot.com/2009/05/introducing-webdriver.html).

2. A lot of the esoteric Selenium commands are gone, replaced with a nice API exposed in http://code.google.com/p/selenium/wiki/JsonWireProtocol. A few changes to help port your code over from Selenium v1.0 to Selenium 2.0 (see example below):
  • The is_text_present() command is no longer provided. You need to use a combination of XPath selectors (i.e. a[text()='abc']) instead of attempting to scan the entire DOM tree for text. Firefox seems to crash if you attempt to do find_element_by_xpath("//html/body").text.find(txt).
  • The get_eval() command that executes any arbitrary JavaScript command is now execute_script(). You must also explicitly define what value you wish to return (i.e. execute_script("return document.activeElement")
  • The open() command is now get()
  • There is no longer a wait_for_condition() command. You have to implement your own to wait for a condition to satisfied.
  • def wait_for_condition(self, js_cmd):
            # Selenium 2 no longer has a wait_for_condition, so we have to create our own polling routine.
            for i in xrange(30):
                ret = self.selenium.execute_script(js_cmd)
                if ret is True:
                    return True
                time.sleep(.25)
    
            if ret is False:
                print "JavaScript command %s did not finish completing (ret_val=%s)" % (js_cmd, ret)
                raise Exception
    
    You can then define a function to wait for jQuery Ajax events are completed by checking the jQuery.active flag. This flag is incremented whenever you invoke a $.ajax() call and decremented when you complete an Ajax call.
    def js_wait():
            wait_for_condition("return jQuery.active === 0")
    
  • You do not need wait_for_page_load() but you do need to invoke implicitly_wait() and set a max timeout (in seconds) to detect when certain DOM elements exist. Selenium 2.0 has no way of detecting when your JavaScript code has finished loading, so you need to create find_element_by_id() or find_element_by_xpath() statement that depend on searching for certain DOM elements that should appear.
3. The Java version provides a backwards compatible API with the new WebDriver toolkit, but the paradigms are slightly different so it helps just to simply port things over. There also isn't a very good selenium package that uses same commands. While you can use the old Selenium 1.0 commands, using the WebDriver-based Selenium 2.0 will considerably improve your performance time.

4. If you're using Firefox on Windows, keep in mind that native extensions are used to trigger click and other mouse-related events. This means that you should attempt to use your web application in a 1024x768 screen, since Selenium opens up the web browser with this size and sends the X/Y coordinates to the browser to fire the mouse-event. If you have DOM elements that are hidden, Selenium v2.0 also checks and triggers an exception. One such issue is in the Django debug toolbar, which can overlap with your buttons and cause events not to fire (See http://hustoknow.blogspot.com/2011/05/selenium-2-and-django-toolbar.html) SauceLabs has a basic intro here: http://saucelabs.com/docs/selenium2

Here's an example of migrating your Selenium 1.0 to Selenium 2.0: Old Selenium v1.0:
sel = selenium
         sel.open("/app/")
         sel.click("css=#org_settings")
         sel.wait_for_page_to_load("30000")
         sel.type("news_title", "My News")
         sel.type("news_content", "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sollicitudin faucibus magna quis varius. Aliquam erat volutpat. Morbi sit amet turpis at tellus consectetur aliquet. Tempus posuere interdum. Fusce eget orci risus.\n\nSed a diam ipsum. Duis faucibus blandit libero eget dictum. In non est justo, sit amet sollicitudin ipsum. Proin consequat, arcu sit amet venenatis dictum.")
         sel.click("news_publish_btn")
         sel.is_text_present("Reach")

Selenium v2.0:
sel = selenium
         sel.selenium.implicitly_wait(10)
         sel.get("/app/")
         sel.find_element_by_id("org_settings").click()
         sel.find_element_by_id("news_title").send_keys("My News")
         self.assertTrue(sel.find_element_by_xpath("//ul[@id='panels']/li[contains(@class, 'wrapper')]/H2[text()='Engagement']"))

No comments:

Post a Comment