Selenium2Library源码解析与扩展(二)

来源:互联网 发布:mac 找不到原始项目 编辑:程序博客网 时间:2024/05/21 06:54

说明:下面说到的一些扩展也适用于AppiumLibrary。

_waiting.py

包括一些等待操作,常用来等待页面加载完成,如:

Input Text    css=.username    adminInput Text    css=.password    123456Wait Until Page Contains Element   css=.loginsuccess#Now [sleep] is not need Click Element    link=xxxx

使用如上方法,即可不用使用sleep等待登录后页面加载,并能在第一时间点击’link=xxxx’元素。
wait_until_page_contains_element()实现如下:

def wait_until_page_contains_element(self, locator, timeout=None, error=None):    """Waits until element specified with `locator` appears on current page.    Fails if `timeout` expires before the element appears.     """    if not error:        error = "Element '%s' did not appear in <TIMEOUT>" % locator    #这里通过_is_element_present()在设置超时范围内一直判断locator匹配元素出现    self._wait_until(timeout, error, self._is_element_present, locator)

_wait_until()实现如下:

def _wait_until(self, timeout, error, function, *args):    error = error.replace('<TIMEOUT>', self._format_timeout(timeout))    def wait_func():        return None if function(*args) else error    #不断执行function(*args)直到wait_func()返回None    self._wait_until_no_error(timeout, wait_func)

然后就是_waiting.py的核心_wait_until_no_error()了,实现如下:

def _wait_until_no_error(self, timeout, wait_func, *args):    #格式化输入的超时时间(从而支持'3s'、'1min'),如果为None,则读取默认配置    timeout = robot.utils.timestr_to_secs(timeout) if timeout is not None else self._timeout_in_secs    maxtime = time.time() + timeout    #死循环    while True:        timeout_error = wait_func(*args)        if not timeout_error: return        #超时,抛出之前设置的error        if time.time() > maxtime:            raise AssertionError(timeout_error)        time.sleep(0.2)

受此启发,在平常编写脚本一般会在每步操作加若干sleep延时,以增加脚本稳定性:

Click Element    link=lkSleep    5sInput Text    css=.input    hello dasshSleep    5sClick Element    css=.btn

当脚本长到一定程度时,使用sleep消耗的时间就非常可观了,扩展方法如下:

def _wait_until_no_error_fixed(self, timeout, fail_raise_error, message, wait_func, *args):    timeout = robot.utils.timestr_to_secs(timeout) if timeout is not None else 15    maxtime = time.time() + timeout    while True:        try:            res = wait_func(*args)        except Exception, e:            timeout_error = True        else:            timeout_error = False        if not timeout_error:            self._info(u"%s ==> PASS." % (message))            return res        if time.time() > maxtime:            if not fail_raise_error:                self._info(u"%s ==> NOT PASS." % (message))                break            else:                raise AssertionError(u"%s ==> FAIL." % (message))                break        time.sleep(0.5)

然后扩展click_element()、input_text()如下:

def input_until_no_error(self, locator, text, message="", timeout=None):     """Try types the given `text` into text field identified by `locator` until no error occurred.    Fails if `timeout` expires before the input success.    """    if not message:        message = "Typing text '%s' into text field '%s'" % (text, locator)    self._wait_until_no_error_fixed(timeout, True, message, self.input_text, locator, text)def click_until_no_error(self, locator, message="", timeout=None):    """Try click element identified by `locator` until no error occurred.    Fails if `timeout` expires before the click success.    """    if not message:        message = "Clicking element '%s'" % locator    self._wait_until_no_error_fixed(timeout, True, message, self.click_element, locator)

从而上面的脚本可以改为如下,实现元素一出现就立即操作:

Click Until No Error    link=xxxxInput Until No Error    css=.input    hello dasshClick Until No Error    css=.btn

第一节提到的几个方法扩展也可以再次扩展如下:

def select_window_until_no_error(self, locator, message="", timeout=None):    """Try selects the window matching locator and return previous window handle until no error occurred.     locator: any of name, title, url, window handle, excluded handle's list, or special words.    """    if not message:        message = "Selecting window '%s'" % locator    self._wait_until_no_error_fixed(timeout, True, message, self.select_window, locator)def title_should_contain_in_time(self, title_piece_list, message="", timeout=None):    """Verifies that current title contains `title_piece_list` in setting time.    Fails if `timeout` expires before find page contains `title_piece_list`.    """    if not isinstance(title_piece_list, list):        piece_list = self._convert_to_list(title_piece_list)    if not message:        message = "Page title should contain '%s'" % (title_piece_list)    self._wait_until_no_error_fixed(timeout, True, message, self.title_should_contain, *piece_list)def click_until_no_error_js(self, locator_css, message="", timeout=None):    """Try JavaScript click element identified by `locator_css` until no error occurred.    Fails if `timeout` expires before find page contains `title_piece_list`.    """    if not message:        message = "JavaScript clicking element '%s'" % locator_css    self._wait_until_no_error_fixed(timeout, True, message, self.click_element_js, locator_css)

title_should_contain_in_time()因为要支持多个片段校验,与之前的title_should_contain()用法有些不一致,使用方法如下:

Title Should Contains    一下    百度    知道    你就Title Should Contains In Time    一下,百度,知道,你就Title Should Contains In Time    [一下,百度,知道,你就]    【校验百度首页】    10s

有时候会遇到这种情况,开发在首页多了个弹出的活动窗,你不得把他X掉;或者进入app,提示有更新,为应对这种情况,扩展如下方法:

def click_if_exists_in_time(self, locator, message="", timeout=None):    """Try click element identified by `locator` in setting time.    Ignore if `timeout` expires before the click success.    """    if not message:        message = "Clicking element '%s'" % locator    self._wait_until_no_error_fixed(timeout, False, message, self.click_element, locator)

当click的对象是链接时,一般会使用link=xxxx,但假如有2个link=xxxx元素,你想点击第2个,又不想换用其它方式定位;或者在app测试里,你会发现开发没有text,没有index给你定位,通过控件类型定位(这样只会匹配第一个元素),又不想写一长串xpath时,扩展如下方法:

def click_nth_element(self, locator, nth=1):    """Click the nth element identified by `locator`."""    try:        nth = int(nth)    except ValueError, e:        raise ValueError("'%s' is not a number" % (nth))    if nth <= 0:        raise ValueError("'nth' must bigger then 0")    elements = self.get_webelements(locator)    self._info("Clicking %dth element '%s'" % (nth, locator))    elements[nth-1].click()def click_nth_until_no_error(self, locator, nth=1, message="", timeout=None):    """Click the nth element identified by `locator` until no error occurred.    Fails if `timeout` expires before the click success.    """    if not message:        message = "Clicking %sth element '%s'" % (nth, locator)    self._wait_until_no_error_fixed(timeout, True, message, self.click_nth_element, locator, nth)

扩展校验内容、元素是否在页面上:

def page_should_contain_text_in_time(self, text, message="", timeout=None):    """Verifies text is not found on the current page in setting time.    Fails if `timeout` expires before find page contain text.    """    if not message:        message = "Page should have contained text '%s'" % (text)    self._wait_until_no_error_fixed(timeout, True, message, self.page_should_contain, text, 'NONE')def page_should_contain_element_in_time(self, locator, message="", timeout=None):    """Verifies element identified by `locator` is not found on the current page in setting time.    Fails if `timeout` expires before find page contain locator element.    """    if not message:        message = "Page should have contained element '%s'" % (locator)    self._wait_until_no_error_fixed(timeout, True, message, self.page_should_contain_element, locator, '', 'NONE')

注:上面这两个方法其实和_waiting.py自带wait_until_page_contains(),wait_until_page_contains_element()类似。

上传文件choose_file()要求路径为绝对路径,有时候不方便填绝对路径时(如部署脚本到Jenkins),扩展如下方法:

def choose_file_until_no_error(self, locator, file_path, message="", timeout=None):    """Try inputs the `file_path` into file input field found by `locator` until no error occurred.     Fails if `timeout` expires before the choose file success.    """    file_path = file_path.encode("utf-8") if os.path.isabs(file_path) else os.path.join(os.getcwd(), file_path.encode("utf-8"))    if not os.path.exists(file_path):        raise ValueError("path file '%s' is not exists." % file_path)    if not message:        message = "Choosing file '%s' from button '%s'" % (file_path, locator)    self._wait_until_no_error_fixed(timeout, True, message, self.choose_file, locator, file_path)

choose_file_until_no_error()判断路径是否为绝对路径,如果不是,则相对为os.getcwd()路径。
注: 路径中建议不含中文

有时候会要获取匹配locator的个数,扩展如下方法:

def get_element_count(self, locator):    """Count elements found by `locator`."""    return len(self.get_webelements(locator))def get_element_count_in_time(self, locator, message="", timeout=None):    """Count elements found by `locator` until result is not 0.    Return 0 if `timeout` expires.    """    return self._wait_until_not_value(timeout, 0, False, message, self.get_element_count, locator)

get_element_count_in_time()主要是解决由于页面加载慢、异步请求未完成locator匹配为0个元素的情况。注意上面调用的是_wait_until_not_value(),而不是前面的_wait_until_no_error_fixed(),源码如下:

def _wait_until_not_value(self, timeout, value, fail_raise_error, message, wait_func, *args):    timeout = robot.utils.timestr_to_secs(timeout) if timeout is not None else 15    maxtime = time.time() + timeout    while True:        res = wait_func(*args)        if res != value:            if message:                self._info(u"%s ==> %s." % (message, res))            return res        if time.time() > maxtime:            if not fail_raise_error:                if message:                    self._info(u"%s ==> %s." % (message, res))                return res            if message:                raise AssertionError(u"%s ==> %s." % (message, res))            else:                raise AssertionError(u"Return ==> %s." % res)            break        time.sleep(0.5)

扩展包地址:
https://github.com/daassh/SeleniumExtend
使用方法:
不再导入Selenium2Library,而是导入此文件。

有些扩展包里的方法没讲请自行阅读或者RF里F5,至此这个系列结束→_→。

过段时间会整理出AppiumExtend.py扩展AppiumLibrary。


by dassh

1 0