Sunday, March 13, 2011

Using FITNesse and Selenium for acceptance testing, example 1

I have alredy written that Fitnesse+SLIM+Selenium is a pretty powerfull tandem to be used for testing web applications. Also I have a blog post about Selenium 2/WebDriver that is much better for testing web applications than first Selenium. In this and upcoming posts I gonna to share my experience of testing complex use cases in my web application using Fitnesse and WebDriver.

For example, there is a list of the SharePoint Sites. You can use checkboxes and a toolbar to select and execute a set of actions for them. Let's do Copy List: select a site and click the Copy List button. It will araise the Select Lists dialog box.
Here you have to select lists what you want to copy and press the Next button. In the next dialog box it's necessary to select target site. Actually a list of these site can be very long. But there is a search here that allows you to narrow a scope: type a name of site that you want to target and press the Search button. Now find the site in the search result and press copy.
Now wait for operation to be completed.
Actually the scenario forks here. It can be completed successfully or not.
If operation is failed, you will see an error description and can click to the SharePoint log files link to see more details.
Here the fitnesse test that checks this scenario.
And here it's code
!|script|
|start|Browser|
|$port=|get port|
|go|${searchCopyUrl}|
|wait for load complete;|
|toggle;|id=${SourceSite}|
|click|id=btnCopyList-button|
!|script|
|wait for element present;|xpath=//input[@id="${list}"]|10|
|toggle;|id="${list}"|
|click|id=dlgMoveListsOKButton-button|
|wait for element present;|id=copy_username_box|10|
!|script|
|type;|id=copy_username_box|${TargetSite}|
|click|id=copy_search_button-button|
|wait for element present;|xpath=//label[@for="${TargetSite}"]|10|
|click|xpath=//label[@for="${TargetSite}"]|
|click;|id=dlgMoveOKButton-button|
|wait for load complete;|
!|script|
|wait for element is displayed;|id=perpetum|
|wait for element is not displayed;|id=perpetum|${copyTimeout}|
|ensure|is element present;|id=trendErrors|
|ensure|is element present;|xpath=//a[text()="SharePoint log files"]|
|click|xpath=//a[text()="SharePoint log files"]|
|wait for load complete;|
|wait for element present|id=result|
|check|eval|return document.getElementById("result").innerText|=~/with [^0] errors./|
!|script|
|close|

As you can see the working element here is the Browser python class
class NullElement:
def __getattr__(self, name):
raise Exception(elementNotFound)
def is_displayed(self):
return 0
class Browser(object):
def __init__(self, browser = 'iexplore'):
if browser == 'chrome':
from selenium.chrome.webdriver import WebDriver
elif browser == 'firefox':
from selenium.firefox.webdriver import WebDriver
else:
from selenium.ie.webdriver import WebDriver
self.wd = WebDriver()
def __getattr__(self, name):
return getattr(self.wd, name)
def get_port(self):
return variables.get_port()
def maximize(self):
script = "window.moveTo(0, 0);window.resizeTo(window.screen.availWidth, window.screen.availHeight);"
self.execute_script(script)
def go(self, url):
self.get(url)
self.maximize()
def get_by_id(self, locator):
type, separator, value = locator.partition('=')
if type.lower() == 'id':
return self.find_element_by_id(value)
def get_by_xpath(self, locator):
type, separator, value = locator.partition('=')
if type.lower() == 'xpath':
return self.find_element_by_xpath(value)
def find_element(self, locator):
element = NullElement()
try:
element = self.get_by_id(locator) or self.get_by_xpath(locator) or log.Except(wrongLocator)
except Exception, e:
log.Except(e)
return element
def toggle(self, locator):
'''
Just toggle a checkbox by given locator
Returns: true or false, that depends on the given checkbox state
Actually it should(or might) contain only two string:
checkbox = self.find_element(locator)
return checkbox.toggle()
but unfortunately I can't get such approach worked in a tests suite.
I mean that a single test works fine, I never got issues, but if a suite is running
I constantly have problems that checkbox is not toggled (have no idea why, very hard to debug in suite)
So I use javascript to toggle a checkbox
'''
script = '''
var elem = $('input[%(locator)s]')
if (elem.length === 0) {
throw('%(exception)s')
}
var state = elem.attr('checked');
elem.attr('checked', !state);
return elem.attr('checked');
''' % { 'locator': locator, 'exception': elementNotFound }
return self.execute_script(script)
def click(self, locator):
elem = self.find_element(locator)
elem.click()
def wait_for(self, condition, time_frame):
wait_time = int(time_frame)
def timeout(wait_time):
return not wait_time
while not condition():
time.sleep(1)
wait_time = wait_time - 1
if timeout(wait_time):
raise Exception('Timed out after %s sec' % time_frame)
def wait_for_element_present(self, locator, timeout=defaultTimeout):
self.wait_for(lambda: self.is_element_present(locator), timeout)
def type(self, locator, text):
elem = self.find_element(locator)
elem.send_keys(text)
def wait_for_element_is_displayed(self, locator, timeout=defaultTimeout):
self.wait_for(lambda: self.is_element_displayed(locator), timeout)
def wait_for_element_is_not_displayed(self, locator, timeout=defaultTimeout):
self.wait_for(lambda: not self.is_element_displayed(locator), timeout)
def is_element_present(self, locator):
element = self.find_element(locator)
return not isinstance(element, NullElement)
def eval(self, script, *args):
return self.execute_script(script, *args)
view raw webdrive-1.py hosted with ❤ by GitHub

No comments:

Post a Comment