【笔记】UI自动化测试关键笔记/个人思考/面试题目 - Selenium基础(持续更新...)

一、【WebDriver】深入理解WebDriver的架构,它如何与浏览器驱动和浏览器交互。

1、Selenium主要组成部分

Selenium主要由以下三部分组成:

  1. Selenium Webdriver:主要负责接受脚本编写的命令,对WEB进行元素查找和元素操作等
  2. Selenium IDE:负责测试脚本的录制
  3. Selenium Grid:Selenium的分布式解决方案,可以将多个用例分发到不同的节点上执行测试

2、为什么Selenium能够支持这么多种浏览器?/ Selenium工作原理

Selenium WebDriver是典型的Server-Client模式(CS结构)

浏览器厂商会提供驱动浏览器操作的中间件(Webdriver),通过向这个中间件发送符合Webdriver协议的请求,可以直接驱动浏览器进行各种操作。

  1. 各类编程语言执行测试用例时,会启动一个RemoteServer,RemoteServer会绑定到对应的浏览器上(以Chrome为例,其他浏览器同理
  2. 用例执行过程中,RemoteServer会一直监听脚本发来的请求(请求需要符合JsonWire协议)
  3. RemoteServer解析完脚本发来的请求后,将对应的操作发送给chromedriver中间件
  4. chromedrvier接收到操作请求后,向被RemoteServer绑定的Chrome浏览器发送操作指令,完成各类WEB操作
  5. WEB操作完成后,浏览器将结果依次返回到chromedriver、RemoteServer、测试脚本,因此测试脚本能够获取到操作结果

二、【元素定位】学习多种元素定位策略(id、name、class、xpath、css选择器等),并理解它们的适用场景和性能影响。

  1. ID定位:通过driver.find_element(By.ID, "id")获取元素。适用于元素具有页面HTML中唯一id的场景,限制因素是当下很多前端开发不愿意写id字段从而导致无法使用id定位获取元素。
  2. name定位:通过driver.find_element(By.NAME, "name")获取元素,适用于元素具有name属性的场景,限制因素同id定位。
  3. class name定位:通过driver.find_element(By.CLASS_NAME, "class_name")获取元素,使用上有很大的限制是,当下主流前端框架(如React)在页面渲染时为了避免元素之间相互影响,通常会在代码的class属性中再加入随机字符串后缀,每次页面加载出来的class name都带有不同的后缀,从而导致无法在脚本中选取合适的class name。此外,因为页面中带有相同class name的通常是一组元素,所以更多使用的是driver.find_elements(By.CLASS_NAME, "class_name")来定位一组元素,然后过滤出合适的元素进行后续操作。
  4. tag name定位:通过driver.find_element(By.TAG_NAME, "tag_name")获取元素,与class name场景类似,因为页面中tag name通常不会只有一个元素,所以更通用的场景是driver.find_elements(By.TAG_NAME, "tag_name")获取一组元素,然后过滤元素进行后续操作。
  5. link text定位:通过driver.find_element(By.LINK_TEXT, "link_text")获取元素,适用于元素tag=a且带有text的情况,通过a标签元素的text全匹配进行元素定位。限制因素,只能对a标签元素进行定位,适用场景不如xpath文本定位广。
  6. partial link text定位:通过driver.find_element(By.PARTIAL_LINK_TEXT, "partial_link_text")获取元素,适用于元素tag=a且带有text的情况,通过a标签元素的text部分匹配进行元素定位。限制因素,只能对a标签元素进行定位,适用场景不如xpath文本定位广。
  7. xpath定位:通过driver.find_element(By.XPATH, "xpath")获取元素,最全能的定位方式,也是我本人最常用的定位方式(最常通过元素文本定位)。适用场景有:多层级定位、文本定位、属性过滤定位等。对于前面class name所提到的随机字符串后缀情况,借助xpath的//*[starts-with(@class, "业务前缀")]语法也可以很好的完成元素定位。
  8. css selector定位:通过driver.find_element(By.CSS_SELECTOR, "css")获取元素,对比xpath定位而言,除了无法完成文本定位,其他定位方式均能支持。性能与xpath定位相比要更好更快。对于前面class name所提到的随机字符串后缀情况,借助css的[class*="业务class"]语法也可以很好的完成元素定位。
    (总的来说,xpath和css_selector基本上可以覆盖所有工作中需要用到的元素定位场景)

三、【操作浏览器/元素】掌握浏览器命令(前进、后退、刷新、截图)、元素交互(点击、输入、滚动、拖拽)等操作。

  1. 浏览器命令
# 浏览器前进一页
driver.forward()
# 浏览器后退一页
driver.back()
# 浏览器刷新
driver.refresh()
# 浏览器截图
driver.save_screenshot('error.png')
  1. 元素交互操作
el = driver.find_element(By.ID, "xxx")
ac = ActionChains(driver)

# 元素三种点击方式
# 元素直接点击
el.click()
# JavaScript点击
driver.execute_script("arguments[0].click();", el)
# ActionChains点击(这种点击方式会自动将元素滚动到视野)
ac.click(el).perform()

# 元素输入两种方式
# 直接输入
el.send_keys("xxx")
# ActionChains输入(模拟大写输入)
ac.key_down(Keys.SHIFT, el).send_keys("selenium").key_up(Keys.SHIFT).perform()

# 将元素滚动到可见范围内
# js滚动
driver.execute_script("arguments[0].scrollIntoView();", el)
# ActionChains滚动
ac.scroll_to_element(scroll_element).perform()

# 拖拽元素A到元素B上
ac.drag_and_drop(el_A, el_B).perform()

四、【Selenium Grid】了解Selenium Grid的概念,学习如何设置和使用Grid进行远程测试

(待补充)

面试题

1. 描述WebDriver是什么,以及它与Selenium Server之间的关系。

(答案见上文)

2. 使用Selenium进行元素定位时,如何定位具有相同属性的多个元素中的第二个元素?

【方法1】使用css定位器,先根据相同属性定位,再加上nth-child(2)

driver.find_element(By.CSS_SELECTOR, 'div[data-label="project_item"]:nth-child(2)')

【方法2】使用xpath定位器,定位到具有相同属性的一组兄弟元素,然后通过索引[2]获取元素

driver.find_element(By.XPATH, '//div[@data-label="project-item"][2]')

【方法3】使用find_elements获取一组元素,然后通过索引

second_element = driver.find_elements(By.XPATH, '//div[@data-label="project-item"]')[2]

3. 解释Selenium中的findElementfindElements方法的区别。

  1. find element用于查找并返回单个页面元素,如果页面中符合条件的元素有多个则返回第一个,如果没有符合条件的则抛出NoSuchElementException
  2. find elements用于查找页面中符合条件的所有元素,返回一个列表。
    1. 如果页面中没有符合条件的元素,也会返回一个空列表,而不是抛出异常。这意味着如果你使用for循环去处理find elements返回的列表,有可能直接跑空什么都没做也不报错。
    2. 一些极端情况下,使用find elements获取元素列表并对每个元素进行处理的过程中,可能出现元素失效的情况

4. 如何在Selenium中执行JavaScript代码,以及它在UI自动化测试中的应用场景是什么?

【场景1】通过JavaScript语句修改HTML标签属性,以达到模拟操作的目的(如鼠标悬停)

driver.get('https://www.taobao.com')
js = "document.querySelector('#J_SiteNavMytaobao').className = 'site-nav-menu site-nav-mytaobao site-nav-multi-menu J_MultiMenu site-nav-menu-hover'"
driver.execute_script(js)  # 模拟鼠标悬停,展开列表元素
driver.find_element(By.XPATH, '//*[text()="已买到的宝贝"]').click()
time.sleep(5)

【场景2】通过JavaScript语句快速完成Selenium操作起来很麻烦的一些行为(如时间控件选择日期)

driver.get('https://www.12306.cn/index/')
new_date = '2024-02-08'
js = f'document.querySelector("#train_date").value = "{new_date}";'
driver.execute_script(js)
time.sleep(3)
js_return = 'return document.querySelector("#train_date").value;'
assert driver.execute_script(js_return) == new_date

【场景3】处理 Message: element click intercepted: Element is not clickable at point(xx, xx). 此类点击失败情况

el = driver.find_element(By.ID, "id")
driver.execute_script("arguments[0].click();", el)

【场景4】将元素滚动到页面视野范围内

el = driver.find_element(By.ID, "id")
driver.execute_script("arguments[0].scrollIntoView();", el)