1129UI自动化测试经验分享-显式等待(三)脚本设置元素等待【干货】

分享到这第三篇了,要说点重点的、大家很愿意知道的:在脚本里到底要如何设置元素等待呢?

一)Web脚本设置元素等待

我和我已离职的同事都喜欢 将元素等待和定位元素结合在一起

同事A的脚本(已做修改):

    def find_element123(self, key, value):
        from selenium.webdriver.support.wait import WebDriverWait

        if key == 'id':
            WebDriverWait(self.driver, 10).until(lambda the_driver: the_driver.find_element_by_id(value).is_displayed())
            return self.driver.find_element_by_id(value)

        if key == 'xpath':
            WebDriverWait(self.driver, 5).until(
                lambda the_driver: the_driver.find_element_by_xpath(value).is_displayed())
            return self.driver.find_element_by_xpath(value)

同事B的脚本(已做修改):

    def element_wait456(self, by, locator, wait_time=5):
        from selenium.webdriver.support.wait import WebDriverWait
        from selenium.webdriver.support import expected_conditions as ec

        if by not in [By.ID, By.XPATH, By.LINK_TEXT, By.CSS_SELECTOR, By.CLASS_NAME, By.TAG_NAME, By.PARTIAL_LINK_TEXT]:
            raise NameError("enter the correct targeting elements:'id','name','class','link_text','xpath','css'")
        WebDriverWait(self.driver, wait_time, 0.5).until(ec.presence_of_element_located((by, locator)))

    def find_element456(self, by, locator, wait_time=5):
        self.element_wait456(by, locator, wait_time)
        return self.driver.find_element(by, locator)

我目前的设置:

    def my_find_Element(self, driver, by, location, thetime=10):
        """
        :param driver: 浏览器驱动
        :param by: 元素定位的方式
        :param location: 元素定位的属性值
        :param thetime: 超时时间,默认10秒

        """
        # 下面两个都可以;第二种比起第一种会 再多一个搜索定位元素,但实际不影响效率,时间基本相同
        return WebDriverWait(driver, thetime).until(ec.visibility_of_element_located((by, location)), '智能等待10秒,定位失败')

        # WebDriverWait(driver, thetime).until(EC.visibility_of_element_located((by, location)), '失败')
        # return driver.find_element(by, location)
  1. 为什么用visibility_of_element_located()?

expected_conditions模块的visibility_of_element_located() 了解一下

在第二篇分享中 讲到visibility_of_element_located()和显式等待结合后,返回值是WebElement;

我的想法是既然已经返回了元素,何必再次去driver.find_element_by_xx(‘XXXX’),所以直接把这个WebElement拿来用。

  1. By类

源码

class By(object):
    """
    Set of supported locator strategies.
    """

    ID = "id"
    XPATH = "xpath"
    LINK_TEXT = "link text"
    PARTIAL_LINK_TEXT = "partial link text"
    NAME = "name"
    TAG_NAME = "tag name"
    CLASS_NAME = "class name"
    CSS_SELECTOR = "css selector"

在Web自动化测试中,对单一元素(不是elements)定位的方式有:find_element_by_id()、find_element_by_xpath()、find_element_by_link_text()、find_element_by_partial_link_text()、find_element_by_name()、find_element_by_tag_name()、find_element_by_class_name()、find_element_by_css_selector()。【8种方法在 selenium包 webdriver模块的WebDriver类下 可以查找到源码】

By类的定位方式 就是 这8种常用的方式。

下图是QQ邮箱的部分用例,全部通过【加强制等待0.5秒 是为了能够看到】

class TestBangZhu(PageBangZhu):
    """上方区域-帮助中心"""

    def test_01(self):
        self.my_find_element(self.driver, By.CSS_SELECTOR, self.bangzhu_css).click()
        time.sleep(0.5)


class TestFanKui(PageFanKui):
    """上方区域-反馈建议"""

    def test_01(self):
        self.my_find_element(self.driver, By.CLASS_NAME, self.fankui_class).click()
        time.sleep(0.5)


class TestHuanFu(PageHuanFu):
    """上方区域-换肤"""

    def test_01(self):
        self.my_find_element(self.driver, By.XPATH, self.huanfu_xpath).click()
        time.sleep(0.5)


class TestSheZhi(PageSheZhi):
    """上方区域-设置"""

    def test_01(self):
        self.my_find_element(self.driver, By.ID, self.shezhi_id).click()
        time.sleep(0.5)


class TestShouYe(PageShouYe):
    """上方区域-邮箱首页"""

    def test_01(self):
        self.my_find_element(self.driver, By.ID, 'frame_html_setting').click()
        time.sleep(0.5)
        self.my_find_element(self.driver, By.PARTIAL_LINK_TEXT, self.shouye_link).click()


class TestTuiChu(PageTuiChu):
    """上方区域-退出"""

    def test_01(self):
        """teardown设置了 账号退出登录"""
        print(self.my_find_element(self.driver, By.LINK_TEXT, self.tuichu_linktext).tag_name)

二)App脚本设置元素等待

web的定位方式就8种,但是app的定位元素的方式多了些。

appium包 webdriver模块的WebDriver类下的 对单一元素(不是elements)定位方式有:【和selenium重复的那8种不写了】find_element_by_image()、find_element_by_ios_uiautomation()、find_element_by_ios_predicate()、find_element_by_ios_class_chain();find_element_by_android_uiautomator()、find_element_by_accessibility_id();

我的安卓app自动化脚本有时候用得是find_element_by_android_uiautomator()、find_element_by_accessibility_id()这两种。

我从元素属性名的角度【text \resource-id\class\content-desc】去想,怎么能 更简洁把显式等待和定位方式结合在一起,想了好久没想好。直到看了WebDriver类下面定位元素方式的源码,才豁然开朗。

    def find_element_by_android_uiautomator(self, uia_string):
        """Finds element by uiautomator in Android.

        :Args:
         - uia_string - The element name in the Android UIAutomator library

        :Usage:
            driver.find_element_by_android_uiautomator('.elements()[1].cells()[2]')
        """
        return self.find_element(by=By.ANDROID_UIAUTOMATOR, value=uia_string)
    def find_element_by_accessibility_id(self, id):
        """Finds an element by accessibility id.

        :Args:
         - id - a string corresponding to a recursive element search using the
         Id/Name that the native Accessibility options utilize

        :Usage:
            driver.find_element_by_accessibility_id()
        """
        return self.find_element(by=By.ACCESSIBILITY_ID, value=id)

知道这两个定位方式的 实际执行方式,再和By类结合起来,简直不能再容易了。

    def my_find_element_android_uiautomator(self, driver, new_str, thetime=10):
        return WebDriverWait(driver, thetime).until(ec.visibility_of_element_located((By.ANDROID_UIAUTOMATOR, new_str)), '失败')

    def my_find_element_accessibility_id(self, driver, location, thetime=10):
        return WebDriverWait(driver, thetime).until(ec.visibility_of_element_located((By.ACCESSIBILITY_ID, location)), '失败')

下图是谷歌市场版 微信的部分用例,全部通过

    def test_05(self):
        """发现-朋友圈"""
        self.my_find_Element(self.driver, By.XPATH, self.faxian_xpath).click()
        self.my_find_Element(self.driver, By.ID, self.pengyouquan_id2).click()      # 1-2s 第二种1-2s

    def test_06(self):
        """通讯录-新的朋友"""
        self.my_find_Element(self.driver, By.XPATH, self.tongxunlu_xpath).click()
        self.my_find_Element(self.driver, By.ID, self.xindepengyou_id).click()  # 1-2s 第二种1-2s

    def test_09(self):
        """通讯录-新的朋友"""
        self.my_find_element_android_uiautomator(self.driver, 'text("通讯录")').click()
        self.my_find_element_android_uiautomator(self.driver, 'text("新的朋友")').click()

    def test_09e(self):
        """通讯录-新的朋友"""
        self.my_find_element_android_uiautomator(self.driver, 'textContains("%s")' % self.tongxunlu_text2).click()
        self.my_find_element_android_uiautomator(self.driver, 'resourceId("%s")' % self.xindepengyou_id).click()

    def test_09f(self):
        """通讯录-微信团队"""
        self.my_find_element_android_uiautomator(self.driver, 'textContains("%s")' % self.tongxunlu_text2).click()
        self.my_find_element_android_uiautomator(self.driver, 'description("%s")' % self.weixintuandui_desc).click()

    def test_09k(self):
        """通讯录-新的朋友  此className第一个是新的朋友"""
        self.my_find_element_android_uiautomator(self.driver, 'textContains("%s")' % self.tongxunlu_text2).click()
        self.my_find_element_android_uiautomator(self.driver, 'className("%s")' % self.gengduo_class).click()       # 找的是 更多功能的class

    def test_10b(self):
        """通讯录-微信团队"""
        self.my_find_element_android_uiautomator(self.driver, 'textContains("%s")' % self.tongxunlu_text2).click()
        self.my_find_element_accessibility_id(self.driver, self.weixintuandui_desc).click()

从元素属性名的角度,到底怎么解决的?find_element_by_android_uiautomator() 了解一下

另外提醒下:
appium1.5以下的版本是可以通过name定位的,新版本从1.5以后都不支持name定位了。
find_element_by_accessibility_id() 取content-desc属性。
find_element_by_id() 取resource-id的值。

三)elements

讲了element,但是有时候遇到 某一属性相同的elements,该怎么才能准确定位到其中的某一位元素呢?

    def my_find_elements(self, driver, by, location, num, thetime=10):
        return WebDriverWait(driver, thetime).until(ec.visibility_of_any_elements_located((by, location)), '失败')[num]

expected_conditions模块 这儿有三种关于检查elements存在的类,【此外until()判断条件 还有 匿名函数 + driver.find_elements()的用法 总共4种方式 】。带大家看下 我选择visibility_of_any_elements_located()的原因:

1.源码

class presence_of_all_elements_located(object):
    """ An expectation for checking that there is at least one element present
    on a web page.
    locator is used to find the element
    returns the list of WebElements once they are located
    """
    def __init__(self, locator):
        self.locator = locator

    def __call__(self, driver):
        return _find_elements(driver, self.locator)

class visibility_of_any_elements_located(object):
    """ An expectation for checking that there is at least one element visible
    on a web page.
    locator is used to find the element
    returns the list of WebElements once they are located
    """
    def __init__(self, locator):
        self.locator = locator

    def __call__(self, driver):
        return [element for element in _find_elements(driver, self.locator) if _element_if_visible(element)]

class visibility_of_all_elements_located(object):
    """ An expectation for checking that all elements are present on the DOM of a
    page and visible. Visibility means that the elements are not only displayed
    but also has a height and width that is greater than 0.
    locator - used to find the elements
    returns the list of WebElements once they are located and visible
    """
    def __init__(self, locator):
        self.locator = locator

    def __call__(self, driver):
        try:
            elements = _find_elements(driver, self.locator)
            for element in elements:
                if _element_if_visible(element, visibility=False):
                    return False
            return elements
        except StaleElementReferenceException:
            return False

这三个类 区别在于at least one element present、at least one element visible、all elements are present on the DOM of a page and visible。

做UI自动化测试,肯定是想元素可见(如果查找的元素算上隐藏的,会更弄不清index),所以presence_of_all_elements_located() 和 find_elements() 排除。

find_elements() 排除的原因:1.presence_of_all_elements_located()的源码中,return _find_elements(driver, self.locator),下面是_find_elements()的源码; 2. 真实的测试结果中,find_elements() 和presence_of_all_elements_located()结果也一样的,从侧面证明第1条原因。

def _find_elements(driver, by):
    try:
        return driver.find_elements(*by)
    except WebDriverException as e:
        raise e

2.全部元素都可见 对于web自动化测试有一点点难度(实际用到操作隐藏元素的机会也不多)

    def test5702b(self):
        """expected_conditions模块里的elements类"""
        from selenium.webdriver.support.wait import WebDriverWait
        from selenium.webdriver.support import expected_conditions as ec
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get("https://www.12306.cn/index/")
        print('开始')

        print('visibility_of_any_elements_located')
        # 标签为a的元素
        print(WebDriverWait(self.driver, 10).until(ec.visibility_of_any_elements_located((By.CSS_SELECTOR, 'a')), '显式等待10秒'))
        # 标签为input type的属性值是text的元素
        print(WebDriverWait(self.driver, 10).until(ec.visibility_of_any_elements_located((By.CSS_SELECTOR, 'input[type="text"]')), '显式等待10秒'))
        # 标签为input type的属性值是hidden的元素 (此类元素 实际是隐藏的)
        try:
            print(WebDriverWait(self.driver, 10).until(ec.visibility_of_any_elements_located((By.CSS_SELECTOR, 'input[type="hidden"]')), '显式等待10秒'))
        except Exception as e111:
            print(e111)
        # 完全不存在的元素
        try:
            print(WebDriverWait(self.driver, 10).until(ec.visibility_of_any_elements_located((By.CSS_SELECTOR, 'a[class="123456789"]')), '显式等待10秒'))
        except Exception as e1:
            print(e1)

        print('visibility_of_all_elements_located')
        try:
            print(WebDriverWait(self.driver, 10).until(ec.visibility_of_all_elements_located((By.CSS_SELECTOR, 'input[type="text"]')), '显式等待10秒'))
        except Exception as e111111:
            print(e111111)
        try:
            print(WebDriverWait(self.driver, 10).until(ec.visibility_of_all_elements_located((By.CSS_SELECTOR, 'a')), '显式等待10秒'))
        except Exception as e222:
            print(e222)
        try:
            print(WebDriverWait(self.driver, 10).until(ec.visibility_of_all_elements_located((By.CSS_SELECTOR, 'input[type="hidden"]')), '显式等待10秒'))
        except Exception as e333:
            print(e333)
        try:
            print(WebDriverWait(self.driver, 10).until(ec.visibility_of_all_elements_located((By.CSS_SELECTOR, 'a[class="123456789"]')), '显式等待10秒'))
        except Exception as e2:
            print(e2)

        print('presence_of_all_elements_located')
        print(WebDriverWait(self.driver, 10).until(ec.presence_of_all_elements_located((By.CSS_SELECTOR, 'a')), '显式等待10秒'))
        print(WebDriverWait(self.driver, 10).until(ec.presence_of_all_elements_located((By.CSS_SELECTOR, 'input[type="text"]')), '显式等待10秒'))
        print(WebDriverWait(self.driver, 10).until(ec.presence_of_all_elements_located((By.CSS_SELECTOR, 'input[type="hidden"]')), '显式等待10秒'))
        try:
            print(WebDriverWait(self.driver, 10).until(ec.presence_of_all_elements_located((By.CSS_SELECTOR, 'a[class="123456789"]')), '显式等待10秒'))
        except Exception as e3:
            print(e3)

        print('find_elements')
        print(WebDriverWait(self.driver, 10).until(lambda the_driver: the_driver.find_elements(By.CSS_SELECTOR, 'a')))
        print(WebDriverWait(self.driver, 10).until(lambda the_driver: the_driver.find_elements(By.CSS_SELECTOR, 'input[type="text"]'), '显式等待10秒'))
        print(WebDriverWait(self.driver, 10).until(lambda the_driver: the_driver.find_elements(By.CSS_SELECTOR, 'input[type="hidden"]'), '显式等待10秒'))
        try:
            print(WebDriverWait(self.driver, 10).until(lambda the_driver: the_driver.find_elements(By.CSS_SELECTOR, 'a[class="123456789"]'), '显式等待10秒'))
        except Exception as e4:
            print(e4)

        time.sleep(1)
        print('结束')
        self.driver.quit()

上图用例的测试结果如下:

在这里插入图片描述

虽然给出了解决定位elements的方案,但我还是推荐 使用其他方式单独唯一定位元素 。尽量少用elements,真的会把握不准确index;

下图是谷歌市场版 微信的关于elements定位的用例,全部通过

    def test_08(self):
        """通讯录-标签"""
        self.my_find_elements(self.driver, By.ID, self.tongxunlu_id, 1).click()     # 0是微信 1是通讯录
        self.my_find_elements(self.driver, By.ID, self.qunliao_id2, 1).click()  # 1是标签

    def test_07c(self):
        """通讯录-群聊"""
        self.my_find_elements(self.driver, By.ID, self.tongxunlu_id, 1).click()     # 0是微信 1是通讯录
        self.my_find_elements(self.driver, By.ID, self.qunliao_id2, 0).click()  # 0是群聊

    def test_02(self):
        """发现-扫一扫"""
        self.my_find_elements(self.driver, By.ID, self.faxian_id, 2).click()
        self.my_find_elements(self.driver, By.ID, self.pengyouquan_id, 3).click()    # 2是朋友圈和扫一扫之间的框  3是扫一扫

    def test_01(self):
        """发现-朋友圈"""
        self.my_find_elements(self.driver, By.ID, self.faxian_id, 2).click()        # 0是微信 1是通讯录
        self.my_find_elements(self.driver, By.ID, self.pengyouquan_id, 1).click()    # 0是前面的框   1是朋友圈

12月份,距离过年更近了,要更努力啊!!!

交流技术 欢迎+QQ 153132336 zy
欢迎关注 微信公众号:紫云小站

猜你喜欢

转载自blog.csdn.net/zyooooxie/article/details/84582144