【UI自动化基础】元素定位
元素定位
元素定位的定义
- 元素定位是指查找元素HTML代码的过程
 - 元素HTML代码指的是从开始标签到结束标签之间的所有代码
 - find_element定位一个元素
 - find_elements定位多个元素
 
元素定位方法
- 
by_id 当元素具有id属性时,可以通过by_id定位元素
方法:driver.find_element_by_id('id属性值')
说明:HTML规定整个HTML文档中元素的id属性值必须是唯一的不允许重复。 - 
by_name 当元素具有name时,可以通过by_name定位元素。
方法:driver.find_element_by_name('name属性值')
说明:HTML规定name属性指定元素名称,name属性值允许重复,可以不唯一。 - 
by_class_name 当元素具有class属性时,可以通过by_class_name定位元素。
方法:driver.find_element_by_class_name('class属性值')
说明:HTML规定class属性指定元素类名,class属性允许有多个值,多个值之间使用空格分隔。元素定位时任选其一即可。class属性值允许重复。 - 
by_tag_name 通过元素标签名定位元素,由于查找到的标签名通常不止一个,所以一般结合find_elements使用。
- 
单数定位
方法:driver.find_element_by_tag_name(‘标签名’)
说明:返回符合条件的第一个元素,结果为元素对象。 - 
复数定位
方法:driver.find_elements_by_tag_name(‘标签名’)
说明:返回符合条件的所有元素,结果为列表,列表中为符合条件的所有元素对象。 
 - 
 - 
by_link_text 通过超链接元素的全部文字定位元素
方法:driver.find_element_by_link_text(‘超链接全部文字’) - 
by_partial_link_text 通过超链接元素的部分文字定位元素
方法:driver.find_element_by_partial_link_text(‘超链接部分文字’) - 
by_xpath
xpath(XML path)用来确定XML文档中某部分位置的语言
方法:driver.find_element_by_xpath(‘xpath表达式’) 
# xpath表达式:
- 绝对路径 
  从根元素到指定元素之间经过的所有元素的层级路径,以/开头
- 相对路径 
  从符合条件的元素开始,以//开头
- 标签+属性
  //标签名[@属性名=”属性值”]
- 层级定位
  //父元素标签名[@父元素属性名=”父元素属性值”]/子元素标签名
- 索引定位
  //父元素标签名[@父元素属性名=”父元素属性值”]/子元素标签名[索引] xpath表达式索引从1开始
- by_css_selector
css(层叠样式表)是用来表现xml或html等文档样式的语言
方法:driver.find_element_by_css_selector(‘css表达式’) 
# css表达式:
- id属性定位 
  # id属性值
- class属性定位 
  .class属性值
- 标签+属性 
  标签名[属性名=”属性值”]
- 层级定位 
  父元素的标签名[父元素属性名=”父元素属性值”] >子元素标签名
- 索引定位
- 匹配父元素中的第n个子元素
  父元素的标签名[父元素属性名=”父元素属性值”] >子元素标签名:nth- child(n)
- 匹配父元素同种标签第n个子元素
  父元素的标签名[父元素属性名=”父元素属性值”] >子元素标签名:nth-of-type(n)
xpath、css定位更多用法:
元素定位策略
- 当页面元素具有id属性时,可以通过by_id定位元素;
 - 当页面元素有超链接需要定位时,通过link_text或partial_link_text定位元素;
 - 当常用方法都无法定位元素时,可以考虑使用xpath或css定位。
 - xpath定位功能很强大,但采用从上到下遍历搜索模式,定位速度慢,尽量少用。
 - css语法简单,采用样式定位,定位速度较快,同时对浏览器的兼容性较好。
 
元素操作
常用操作
- 输入
元素对象.send.keys(‘输入内容’) - 点击
元素对象.click() - 其他操作
 - 清空
元素对象.clear() - 获取元素文字
元素对象.text - 获取元素属性值
元素对象.get_attribute(‘属性值’) - 判断元素是否被选中,如果被选择返回True,否则返回False
元素对象.is_selected() 
下拉菜单
普通方法定位
直接定位下拉菜单中的选项,并点击
Select类方法定位
- 定位<select>标签实现的下拉菜单元素
webelement = driver.find_element_by_XXX(' ') 提供的八种方法随便选择
2.使用Select类中提供的方法选择菜单选项 
- 通过select类提供的方法,选择下拉菜单中的具体选项。
Select(webelement).select_by_index(索引) - 通过value属性值选择菜单选项。
Select(webelement).select_by_value('value属性值') - 通过文字选择菜单选项。
Select(webelement).select_by_visible_text(文字) 
说明:Select类只针对于<select>标签实现的下拉菜单有效
单选框和复选框
单选框
定位并点击
复选框
复选框的单选
- 定位并点击
复选框的全选 - 使用find_elements复数定位,对结果进行遍历点击,但需要注意的是,再次点击已选择的复选框相当于取消,所以点击之前先要判断复选框是否已被选择
 
多窗口
在页面操作过程中,点击超链接有时会打开一个新窗口,如果要定位新窗口中的元素需要使用switch_to.window()切换窗口。
- 获取所有窗口句柄(窗口id) 
driver.window_handles - 获取当前窗口句柄 
driver.current_window_handle - 切换窗口 
driver.switch_to.window(driver.window_handles[索引]) 
    def base_switch_handle(self):
        """窗口句柄切换"""
        handle = self.driver.current_window_handle   # 当前窗体句柄
        print("当前窗体句柄:", handle)
        handles = self.driver.window_handles    # 所有窗体句柄
        print("窗体句柄", handles)       # 获取浏览器所有窗体的句柄
        self.driver.switch_to.window(handles[-1])   # 切换至最新窗口句柄
鼠标事件
ActionChains类鼠标操作方法
- 
鼠标拖拽
ActionChains(drive).drag_and_drop(起始元素,目标元素).perform() - 
鼠标悬停
ActionChains(drive).move_to_element(目标元素).perform() 
调用ActionChains类中的鼠标操作方法时,事件不会立即执行,而是会将事件保存到一个队列中,需要调用perform()方法,队列中的事件才会执行
滚动条
当页面元素超过一屏时,有时候可能无法定位屏幕下方的元素,这时就需要拖动滚动条,使被定位的元素出现在当前屏幕上,而selenium无法定位和操作滚动条,需要借助js脚本。
- js脚本实现浏览器中打开新窗口 
window.open(“网址”) - js脚本实现拖动滚动条 
window.scrollBy(水平距离,垂直距离) - selenium执行js脚本 
driver.execute_script('js脚本') 
表单嵌套页面
在web应用程序中会经常遇见iframe/frame表单嵌套页面,selenium无法定位嵌套页面中的元素,需要使用switch_to.frame()切换到表单的内嵌页面
1.进入表单内嵌页面
driver.switch_to.frame('id属性值') # iframe/frame标签元素的id属性值
# driver.switch_to.frame('name属性值') # iframe/frame标签元素的name属性值
# driver.switch_to.frame(索引) # iframe/frame标签元素的索引
element = driver.find_element_by_css_selector('#if2') # 先定位元素
driver.switch_to.frame(element) # iframe/frame标签元素对象
2.返回最外层页面
driver.switch_to.default_content()
3. 返回上一层页面
driver.switch_to.parent_frame()
js弹窗
JavaScript中有警告框、确认框、提示框三种弹窗。
selenium使用 switch_to.alert 定位js弹窗,然后调用相应的方法对js弹窗进行操作。
- 点击js弹窗上的确定按钮 
driver.switch_to.alert.accept() - 点击js弹窗上的取消按钮 
driver.switch_to.alert.dismiss() - 获取js弹窗上的文字 
driver.switch_to.alert.text - 向js弹窗输入内容 
driver.switch_to.alert.send_keys("") 
上传文件
对于标签名为input并且type属性值为file的上传功能,selenium通过send_keys(文件路径)来实现文件的上传。
验证码处理
web自动化登录验证码处理方法
- 找开发改测试环境,将验证码的代码注释掉
 - 找开发设置万能码,只适用于输入型的验证
 - 使用cookie跳过登录
 - 验证码识别技术OCR
 
元素等待
在页面中定位元素时如果元素未出现,则在指定时间内一直等待的过程。
在web自动化中引入等待的机制,可以保证代码的稳定性,使代码不会受到网络、电脑等因素的影响。
Selenium中有三种等待方式:强制等待、隐式等待、显式等待
强制等待(sleep)
from time import sleep
sleep(3)  # 强制等待3秒
# 强行让浏览器等待指定时间,然后再进行下一步操作。
# 缺点:
  由于Web加载的速度取决于测试的硬件、网速、服务器的响应时间等因素,如果等待时间太长,容易造成时间浪费,如果等待时间太短又可能会造成在web还没有加载完所需要定位的element,而出现报错。
  由于等待时间无法确定,使用太多的sleep会影响运行速度,大大地降低效率,所以建议测试中尽量少使用强制等待。
隐式等待(implicitly_wait)
# 隐式等待10s
driver.implicitly_wait(10) 
# 在指定时间内,如果页面中的元素全部加载完成,则进行下一步操作,否则直到时间结束,然后抛出异常
# 隐式等待是全局的是针对所有元素,设置等待时间如10秒,如果10秒内出现,则继续向下,否则抛异常。可以理解为在10秒以内,不停刷新看元素是否加载出来。
# 使用场景:
隐式等待只需要 声明一次,一般在打开浏览器后进行声明。声明之后对 整个drvier的生命周期 都有效,后面不用重复声明。
 隐式等待存在一个问题,那就是程序会一直等待整个页面加载完成, 也就是一般情况下你看到浏览器标签栏那个小圈不再转,才会执行下一步,但有时候页面想要的元素早就加载完成了,但是因为 个别js之类的东西特别慢 ,仍得等到页面全部完成才能执行下一步。
显式等待(expected_conditions)
程序每隔一段时间就检查一次元素是否出现等条件,如果元素出现,则进行下一步操作,否则继续等待,直到超过最长等待时间,然后抛出超时异常。
显示等待是单独针对某个元素,设置一个等待时间如5秒,每隔0.5秒检查一次是否出现,如果在5秒之前任何时候出现,则继续向下,一般需要配合该类的 until() 和 until_not() 方法一起用,直到超过设置的最长时间,然后抛出超时错误 TimeoutException。
以下介绍几个常用的方法:
- 判断element是否可见
visibility_of_element_located(locator) (可见代表element非隐藏,并且element宽和高都不等于 0) 
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
# 示例
target = EC.visibility_of_element_located(By.ID,'user')
# 配合until()使用(等待元素可见)
# 5表示 最长超时时间,默认以秒为单位; 1表示检测的间隔步长,在等待期间,每隔一定时间(默认0.5秒),调用until或until_not里的方法,直到它返回True或False.
WebDriverWait(driver, 5, 1).until(EC.visibility_of_element_located(By.ID,'user'))
# 配合until_not()使用(等待元素不可见)
WebDriverWait(driver, 5, 1).until_not(EC.visibility_of_element_located(By.ID,'user'))
# 在类中封装为一个函数
    def wait_eleLocated(self, loc, timeout=30, poll_frequency=0.5, model=None):
        """
        :param loc:元素定位表达;元组类型,表达方式(元素定位类型,元素定位方法),示例:(By.ID, "kw")
        :param timeout:超时时间
        :param poll_frequency:轮询频率
        :param model:等待失败时,截图操作,图片文件中需要表达的功能标注
        :return:None
        """
        self.logger.info(f'等待"{model}"元素,定位方式:{loc}')
        try:
            start = datetime.now()
            WebDriverWait(self.driver, timeout, poll_frequency).until(EC.visibility_of_element_located(loc))
            end = datetime.now()
            self.logger.info(f'等待"{model}"时长:{end - start}')
        except TimeoutException:
            self.logger.exception(f'等待"{model}"元素失败,定位方式:{loc}')
            # 截图
            self.save_webImgs(f"等待元素[{model}]出现异常")
            raise
- 判断某个element是否被加载到dom树
presence_of_element_located(locator)---并不代表该element一定可见 
方法:WebDriverWait(driver,等待时间).until(EC.presence_of_element_located((By.ID, '属性值'))).send_keys('输入的东西')
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
EC.presence_of_element_located(By.ID,'user')
# 类封装
class BaseElement:
    def __init__(self, dr):
        """初始化"""
        self.driver = dr
    def base_find_new(self, loc, timeout=5, poll_frequency=0.5, model=None):
        """获取查找元素"""
        # 显示等待
        try:
            start = datetime.now()
            obj = WebDriverWait(self.driver,
                                timeout=timeout,
                                poll_frequency=poll_frequency) \
                .until(ec.presence_of_element_located(*loc))
            end = datetime.now()
            logger.info(f"等待{model}时长:{end-start}")
            return obj
        except Exception as e:
            # self.driver.find_element(By.XPATH, "随机事件异常处理").click()  # 针对任意时间出现的弹窗处理(如:广告弹框、好评弹框等)
            logger.exception(f"等待{model}元素失败,定位方式:{loc}\n", str(e))
            # 截图
            self.get_screen_shot(f"等待元素{model}出现异常")
            raise
- 判断某个element中是否可见并且可点击
element_to_be_clickable(locator) 
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
target = EC.element_to_be_clickable(By.ID,'user')
- 判断某个element是否被选中
element_to_be_selected(element) ---一般用在下拉列表 
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
element = driver.find_element_by_class_name('selector')
EC.element_to_be_selected(element)
- 查找find_element用法
 
class BaseElement:
    def __init__(self, dr):
        """初始化"""
        self.driver = dr
    def base_find(self, loc, timeout=5, poll_frequency=0.5):
        """获取查找元素"""
        # 显示等待
        try:
            obj = WebDriverWait(self.driver,
                                timeout=timeout,
                                poll_frequency=poll_frequency) \
                .until(lambda x: x.find_element(*loc))
            return obj
        except Exception as e:
            # self.driver.find_element(By.XPATH, "随机事件异常处理").click()  # 针对任意时间出现的弹窗处理(如:广告弹框、好评弹框等)
            logger.error(f"元素未找到{loc}", str(e))
            self.get_screen_shot("元素未找到")
进群交流、获取更多干货, 请关注微信公众号:

> > > 咨询交流、进群,请加微信,备注来意:sanshu1318 (←点击获取二维码)
> > > 学习路线+测试实用干货精选汇总:
https://www.cnblogs.com/upstudy/p/15859768.html
> > > 【自动化测试实战】python+requests+Pytest+Excel+Allure,测试都在学的热门技术:
https://www.cnblogs.com/upstudy/p/15921045.html
> > > 【热门测试技术,建议收藏备用】项目实战、简历、笔试题、面试题、职业规划:
https://www.cnblogs.com/upstudy/p/15901367.html
> > > 声明:如有侵权,请联系删除。
============================= 升职加薪 ==========================
更多干货,正在挤时间不断更新中,敬请关注+期待。
                
            
        
浙公网安备 33010602011771号