分享到这第三篇了,要说点重点的、大家很愿意知道的:在脚本里到底要如何设置元素等待呢?
我和我已离职的同事都喜欢 将元素等待和定位元素结合在一起。
同事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)
expected_conditions模块的visibility_of_element_located() 了解一下
在第二篇分享中 讲到visibility_of_element_located()和显式等待结合后,返回值是WebElement;
我的想法是既然已经返回了元素,何必再次去driver.find_element_by_xx(‘XXXX’),所以直接把这个WebElement拿来用。
源码
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)
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的值。
讲了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()的原因:
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
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
个人博客 https://blog.csdn.net/zyooooxie