1. 程式人生 > >1129UI自動化測試經驗分享-顯式等待(三)指令碼設定元素等待【乾貨】

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
歡迎關注 微信公眾號:紫雲小站