Selenium UI自动化测试中元素定位不到的原因和解决方法汇总

1、总览

原因解决方法
没有打开正确的网址填写正确的网址
定位器选择错误选择合适的定位器
定位表达式错误简单粗暴:F12 copy或手写定位调试
元素嵌套在iframe中1,切换到iframe中:driver.switch_to.frame(’ iframe的id或name值 ');2,再进行元素定位
元素在新窗口中1,获取打开的多个窗口句柄:handles = driver.window_handles;2,切换到新窗口中: driver.switch_to.window(handles[-1])
页面元素没有及时加载1,加等待,不要加的太少,加10s,如果10秒还找不到说明不是因为页面加载导致的元素找不到;2,确定是页面元素没有及时加载原因后,可以使用以下三种等待方式,详见1.2
页面元素不可见或不可点击1,使用JavaScript实现元素定位和动作执行;2,使用鼠标事件ActionChains来操作;3,如果是被伪元素遮挡了原本的元素,可以直接定位到伪元素上进行点击操作 。详见1.3
页面元素是动态的1.根据其他静态属性定位;2.根据元素属性值模糊匹配定位。 详见1.4
脚本流程与实际不符调整脚本以符合实际业务流程

2、元素定位不到的原因之【页面元素没有及时加载】

问题定位思路:

  1. 加等待,不要加的太少,加10s,如果10秒还找不到说明不是因为页面加载导致的元素找不到
  2. 确定是页面元素没有及时加载原因后,可以使用以下三种等待方式
    (1)调试代码使用强制等待:sleep(10) ;
    (2)作用于全局使用隐式等待:driver.implicitly_wait(10)
    隐式等待一般在实例化dirver之后就设置,在服务端等待,作用于全局,也就是在driver的整个生命周期中生效。隐式等待是动态的查找所有元素,默认每隔0.5s轮询一次(也可以手动设置间隔时间),要查找的元素未加载完就继续等待,如果要查找的元素在规定的timeout时间内提前加载完,则结束等待,执行下一步操作;如果要查找的元素在规定的timeout时间内没有加载完,则会抛出异常。
    (3)提升用例执行效率使用显式等待
    显式等待在客户端等待,作用于指定元素,也就是只有在写了显式等待的语句中生效。显式等待也是动态的查找元素,要查找的元素未加载完就继续等待,如果要查找的元素在规定的timeout时间内提前加载完,则结束等待,执行下一步操作;如果要查找的元素在规定的timeout时间内没有加载完,则会抛出异常。需要用到两个类:WebDriverWait 和 expected_conditions

为什么要使用显式等待?

先来了解一下一般html页面上元素的呈现:

  • title出现 首先出现title
  • dom树出现 存在presence,但还不完整
  • css出现 可见visibility
  • js出现,js特效执行 可点击clickable

html文档是自上而下进行加载的,有些js通过异步加载的方式来完成js的加载
样式表下载完成之后会跟之前的样式表一起进行解析,会对之前的元素重新渲染

基于以上原理,我们知道,我们看到元素已经显示出来了,但是元素的某些属性可能还没有加载完全,比如元素是否是可见visibility、元素是否是可点击clickable,如果用隐式等待只能判断元素是否在dom树中出现,它无法判断元素是否可见,元素是否可点击,因此我们需要使用显式等待

显式等待可以处理隐式等待无法解决的一些问题,比如:上传文件(可以设置长一点),文件上传需要设置20s以上,但是如果设置隐式等待,它会在每个find方法都等这么长时间,一旦发现没有找到元素,就会等20s以后才抛出异常,影响case的执行效率,这时候就需要用显式等待,显式等待可以设置的长一点

设置显式等待的两种方法:

方法1:使用selenium自带的WebDriverWait 和 expected_conditions类,expected_conditions里面有很多定义好的判断条件供until使用
示例:

from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

locator = (By.XPATH, '//*[@id=”current_price“]')
WebDriverWait(driver, 20, 0.5).until(EC.presence_of_element_located(locator))

方法2:使用lambda表达式
示例:

from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By

locator = (By.XPATH, '//*[@id=”current_price“]')
ele = WebDriverWait(self.driver, 20).until(lambda x: x.find_element(*locator))

3、元素定位不到的原因之【页面元素不可见或不可点击】

此处所说的【页面元素不可见或不可点击】的前提条件是:元素已经加载完全了!
如果页面元素还没加载完全,导致的不可见或不可点击,我们可以通过上面1.2的显式等待方法来处理

有一种情况是:页面元素明明已经加载完全了,也定位到了,但是就是执行无效,比如点击无效等,我们又该如何处理呢?

接下来我们就以testerhome.com (https://testerhome.com/account/sign_in)的登录页面的记住密码勾选框为例
问题:直接定位元素后用.click()方法会报错:

self.driver.find_element_by_xpath('//*[@id="user_remember_me"]').click()

报错信息如下:
在这里插入图片描述
分析报错信息:
定位表达式id="user_remember_me"确实是定位到了目标元素,但是报错提示:该元素:id="user_remember_me"不可点击,而其他元素:class="custom-control-label"却被接受点击了
在这里插入图片描述
再来看看页面源码:
在这里插入图片描述
既然是因为元素不可点击导致的问题,那自然也有对应的解决办法,以下列出了可行方案,可供参考
在这里插入图片描述

4、元素定位不到的原因之【页面元素是动态的】

怎么判断元素是否是动态?

  1. 首先应先确定时间等待没问题、且没有打开新页面、没有alert、没有frame,元素可见或可点击

  2. 此时最有可能的原因就是元素是动态的,元素动态有分两种:
    a.元素的属性是动态的
    可以尝试刷新一下网页查看该元素前后有没有变化,如果属性值改变了,即是动态属性了,较为常见的是元素的id属性,或class属性里有拼接一串数字的,就很有可能是动态元素了,如下图:
    在这里插入图片描述
    b.元素的位置是动态的
    有些元素是因为某些框架技术自动生成的动态元素

    比如下图中【添加收货地址】按钮,
    在这里插入图片描述
    未添加任何地址前,该元素是在最右侧://*[@id=“address-box”]/div[1]
    添加第一个地址后,该元素位置往右偏移了://*[@id=“address-box”]/div[2]
    添加第二个地址后,该元素位置再往右偏移://*[@id=“address-box”]/div[3]
    以此类推…

    又比如很多web网站的轮播图也是属于动态元素…

动态元素怎么定位?
针对 a.元素的属性是动态的的解决方法:

  1. 根据其他静态属性定位
    比如如果id属性是动态的,而class属性是静态的,则用class属性来定位
  2. 根据元素属性值模糊匹配定位
    如果有固定前缀、后缀值,可以通过 xpath 的模糊匹配方法
    driver.find_element_by_xpath(“//标签名[contains (@属性,‘属性值’)]”)
    driver.find_element_by_xpath(“//标签名[starts-with (@属性,‘属性值’)]”)
    driver.find_element_by_xpath(“//标签名[end-with (@属性,‘属性值’)]”)
  3. 根据父级、子级或同级元素定位
    属性值完全随机的话,可以通过 xpath、css 选择器找到它的父级、子级、同级 元素,再进行定位
    ps:css 不能找到父级元素
    元素xpathcss
    父级//div/…/span
    子级//div/spandiv > span
    同级//div/following-siblingdiv + span

针对b.元素的位置是动态的的解决方法:
可以通过遍历获取对应的下标,然后取最大下标进行定位