是否有表单嵌套
在Web应用中经常会遇到iframe/frame表单嵌套页面的应用,WebDriver只能在一个页面上对元素识别与定位,对于iframe/frame表单内嵌页面上的元素无法直接定位。
这时就需要通过switch_to.frame()方法将当前定位的主体切换为iframe/frame表单的内嵌页面中。
单个表单嵌套:
- #www.testclass.cn
- #Altumn
- #2018-11-5
- from selenium import webdriver
- driver = webdriver.Chrome()
- driver.get("https://www.testlcass.cn")
- driver.switch_to.frame("wpCalendarLayer")#输入iframe/frame的name属性;
- driver.find_element_by_id("id").click()
- driver.switch_to.default_content()#跳回最外层的页面;
- driver.quit()
复制代码
多个表单嵌套:
有时也会遇到多个表单的嵌套,这样我们就需要一层层的跳转,从第一层跳转到要定位元素所在的那层表单。处理完业务如果需要跳转到其他层表单,首先需要跳转到最外层的页面,然后再逐一跳转表单。
- #www.testclass.cn
- #Altumn
- #2018-11-5
- from selenium import webdriver
- driver = webdriver.Chrome()
- driver.get("https://www.testlcass.cn")
- driver.switch_to.frame("main")#输入iframe/frame的name属性;跳转第一层表单;
- driver.switch_to.frame("wpCalendarLayer")#输入iframe/frame的name属性;跳转第二层表单;
- driver.find_element_by_id("id").click()
- driver.switch_to.default_content()#跳回最外层的页面;
- driver.quit()
复制代码
iframe/frame没有可用的属性:
switch_to.frame()方法通过表单的id或name属性进行定位。但是有时iframe/frame没有可用的id和name属性值,那么这时候我们可以根据以下方式进行元素定位:
- #www.testclass.cn
- #Altumn
- #2018-11-5
- from selenium import webdriver
- driver = webdriver.Chrome()
- driver.get("https://www.testlcass.cn")
- #先通过xpth定位到iframe
- main = driver.find_element_by_xpath('//*[@name="main"]')
- #再将定位对象传给switch_to.frame()方法
- driver.switch_to.frame(main)
- ……
- driver.switch_to.parent_frame()#切换到上一层的frame,对于层层嵌套的frame很有用
复制代码
元素不可见
在UI自动化测试中,有时会遇到页面元素无法定位的问题,包括xpath等方法都无法定位,是因为前端元素被设置为不可见导致。
通过F12工具查看元素发现元素的display:none方法是设置元素不可见,这就是导致为什么通过定位页面元素无法定位的原因。那么这时候我们可以通过JS方法实现修改元素属性值,进而达到元素实现可见或者不可见的功能。
例如:百度主页功能菜单栏"更多产品"一项,默认属性是可见display:block,我们接下来可以通过JS操作元素属性修改,操作步骤如下:
1.进入百度首页;
2.修改"更多产品"一项的属性值display:block为display:none;
3.修改"更多产品"一项的属性值display:none为display:block;
4.点击"更多产品";
详细操作代码:
- #Baidu.py
- #www.testclass.cn
- #Altumn
- #2018-11-5
- from selenium import webdriver
- import time
- driver = webdriver.Chrome()
- driver.implicitly_wait(10)
- driver.get("http://www.baidu.com")
- #修改display属性为'none',把"更多产品"功能按钮隐藏;
- js = "document.getElementsByName('tj_briicon')[0].style.display='none';"
- # 调用js脚本
- driver.execute_script(js)
- time.sleep(5)
- # 修改display属性为'block',把"更多产品"功能按钮显示;
- js = "document.getElementsByName('tj_briicon')[0].style.display='block';"
- # 调用js脚本
- driver.execute_script(js)
- #判断元素是否在页面上可见;
- element = driver.find_element_by_name("tj_briicon")
- print(element.is_displayed)
- driver.find_element_by_name("tj_briicon").click()
复制代码
页面元素不可见的元素虽然在界面上不显示,但是在DOM树中,这些元素webdriver也可以找到。
HTML DOM的操作方法有很多,本文在此暂不敖述,以后会出详细使用方法。例如:
- document.getElementsByClassName() #返回文档中所有指定类名的元素集合,作为 NodeList 对象。
- document.getElementById() #返回对拥有指定 id 的第一个对象的引用。
- document.getElementsByName() #返回带有指定名称的对象集合。
- document.getElementsByTagName() #返回带有指定标签名的对象集合。
复制代码
元素不满足条件
有些元素存在但是不可见;或者你的操作步骤缺少前提步骤。
例如,百度主页工具栏的"搜索设置"功能,默认就是不可见的。这时我们需要鼠标悬停操作,让设置的下拉内容显示出来。所以,鼠标悬停“设置”链接上就是前提条件。
如果把鼠标从"搜索设置"上面移开,你会发现display属性值为none,它所在的整个[div]标签就是因为这个属性的变化而隐藏的。
如下所示,把鼠标从"搜索设置"上面移开:
如下所示,把鼠标移动到"搜索设置"上面,display属性值为block:
详细鼠标悬停操作代码如下:
- #www.testclass.cn
- #Altumn
- #2018-11-5
- from selenium import webdriver
- # 引入 ActionChains 类
- from selenium.webdriver.common.action_chains import ActionChains
- driver = webdriver.Chrome()
- driver.get("https://www.baidu.cn")
- # 定位到要悬停的元素
- above = driver.find_element_by_link_text("设置")
- # 对定位到的元素执行鼠标悬停操作
- ActionChains(driver).move_to_element(above).perform()
复制代码
元素的定位方法有很多,定位方式需要灵活运用。
有时在定位元素的时候,明明感觉自己的用法没有错,脚本语法也完全没错,可是为什么定位不到呢?
无论用什么定位方法,可以先使用find_elements_by_xxx()来定位一组元素。
如果统计结果是0,说明你的定位方法找不到任何元素,元素本身不存在。
如果大于1,说明这种定位方法定位到的元素不是唯一的。那么就需要把元素打印出来,查看第几个是你所需要的元素:
详细元素定位代码如下:
- #Baidu.py
- #www.testclass.cn
- #Altumn
- #2018-11-5
- from selenium import webdriver
- import time
- driver = webdriver.Chrome()
- driver.implicitly_wait(10)
- driver.get("https://www.baidu.com/")
- driver.maximize_window()
- #定位一组元素;
- elements=driver.find_elements_by_name("tj_settingicon")
- print(len(elements))
- #循环打印出每个元素的属性值;
- for i in range(len(elements)):
- print("第" + str(i) + "个元素")
- print(elements[i].get_attribute("name"))
- print(elements[i].get_attribute("class"))
复制代码
输出结果如下所示:
- PS C:\Users\WangXiao\Desktop\mystuff> cd 'C:\Users\WangXiao\Desktop\mystuff'; ${env:PYTHONIOENCODING}='UTF-8'; ${env:PYTHONUNBUFFERED}='1'; & 'C:\Users\WangXiao\AppData\Local\Programs\Python\Python36\python.exe' 'c:\Users\WangXiao\.vscode\extensions\ms-python.python-2018.7.1\pythonFiles\PythonTools\visualstudio_py_launcher.py' 'C:\Users\WangXiao\Desktop\mystuff' '51070' '34806ad9-833a-4524-8cd6-18ca4aa74f14' 'RedirectOutput,RedirectOutput' 'c:\Users\WangXiao\Desktop\mystuff\python2.py'
- DevTools listening on ws://127.0.0.1:12958/devtools/browser/589110e2-92eb-46e5-924c-1b8fbded0a2f
- 2
- 第0个元素
- tj_settingicon
- pf
- 第1个元素
- tj_settingicon
- pf
复制代码这样你就可以通过元素的属性值判断你要定位的元素:
- driver.find_elements_by_name("tj_settingicon")[0].click()
- driver.find_elements_by_name("tj_settingicon")[1].click()
复制代码
元素动态id属性
有时,你要定位的元素属性是动态的,即每次打开页面该元素的id或者class等属性是动态生成的。
元素定位时会抛出NoSuchElementException的错误。怎么判断元素属性是否是动态?
很简单,一般看到元素属性里有拼接一串数字的,就很有可能是动态的。想要分辨,刷新一下浏览器再看该元素,属性值中的数字串改变了,即是动态属性了。在此介绍一个解决方法,使用xpath根据动态元素属性进行定位:
xpath中提供了三个非常好的方法来为我们定位部分属性值:
- driver.find_element_by_xpath("//input[contains(@id, 'bt-class')]") # id属性包含'bt-class',且固定不变;
- driver.find_element_by_xpath("//input[starts-with(@id, 'bt-class')]") # id属性开头为'bt-class',且固定不变;
- driver.find_element_by_xpath("//input[ends-with(@id, 'bt-class')]") # id属性结尾是'bt-class',且固定不变;
复制代码
元素未出现就进行了操作
有时,明明单步调试的元素可以定位到,并且可以正常操作,但是在跑测试案例的时候,反而报错。这时就需要考虑是否界面的切换,或者功能的跳转导致元素加载缓慢。所以我们需要加上元素等待;
同样,WebDriver提供了两种类型的元素等待方法:显式等待和隐式等待。
显式等待:
显式等待使WebdDriver等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException);
- #Baidu.py
- #www.testclass.cn
- #Altumn
- #2018-11-5
- from selenium import webdriver
- from selenium.webdriver.common.by import By
- from selenium.webdriver.support.ui import WebDriverWait
- from selenium.webdriver.support import expected_conditions as EC
- driver = webdriver.Firefox()
- driver.get("http://www.baidu.com")
- element = WebDriverWait(driver, 5, 0.5).until(
- EC.presence_of_element_located((By.ID, "kw"))
- )
- element.send_keys('selenium')
- driver.quit()
复制代码
WebDriverWait类是由WebDirver 提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。