Selenium学习笔记
一、概述
1. 什么是自动化测试
- 让程序代替人工去验证系统功能的过程
2. 自动化测试能解决什么问题
-
回归测试:项目在发新版本之后,对项目之前的功能进行验证
-
压力测试:可理解为多用户同时去操作软件,统计软件服务器处理多用户请求的能力
-
兼容性测试:浏览器、分辨率、操作系统
-
提高测试效率,保证产品质量
3. 优点
-
较少的时间内运行更多的测试用例【提高效率】
-
自动化脚本可重复运行
-
减少人为的错误【提高产品质量】
-
克服手工测试的局限性【如:获取图片大小】
-
规格统一标准
4. 误区
-
自动化测试可完全代替手工测试
-
自动化测试一定比手工测试厉害
-
自动化测试可以发掘更多的 BUG
-
自动化测试适用于所有功能
5. 分类
-
Web - 自动化测试
-
移动 - 自动化测试【app 自动化】
-
接口 - 自动化测试(工具、代码)
-
单元测试 - 自动化测试
6. 什么是 Web 自动化测试
-
让程序代替人工自动验证 Web 项目功能的过程
-
Web 自动化测试属于黑盒测试
7. 什么 Web 项目适合做自动化测试
-
需求变动不频繁
-
项目周期长
-
项目需要回归测试
8. Web 自动化测试在什么阶段开始
-
功能测试完毕(手工测试之后)
-
时间问题
-
功能不完善
-
9. 主流的 Web 自动化测试工具
-
QTP:商业化的功能测试工具,收费,支持 web、桌面自动化测试
-
Selenium:开源的 Web 自动化测试工具,免费,主要做功能测试,只支持 web 项目
-
Robot framework:基于 Python 可扩展的关键字驱动的测试自动化框架(2014 年停止更新)
10. Selenium 特点
-
开源软件:源代码开放,可根据需要来增加工具的某些功能
-
跨平台:linux、windows、mac
-
支持多种浏览器:Firefox、Chrome、IE、Edge、Opera、Safari 等
-
支持多种语言:Python、Java、C#、JavaScript、Ruby、PHP 等
-
成熟稳定:目前已被 Google、百度、腾讯等公司广泛使用
-
功能强大:能够实现类似商业工具的大部分功能,因为开性,可实现定制化功能
11. 科普 Path
-
说明:指定系统搜索的目录
-
dos 命令默认搜索顺序:
-
检测是否为内部命令
-
检测是否为当前目录下可执行文件
-
检测 Path 环境变量指定的目录
-
-
如果以上搜索目录都检测不到输入的命令或可执行文件,系统会抛出“不是内部或外部命令...”
二、Selenium
1. 安装、查看、卸载
pip install selenium
pip show selenium
pip uninstall selenium
2. 安装浏览器驱动
-
驱动安装完要配置环境,直接放在 Python 目录下也可以
-
注:浏览器驱动必须和浏览器版本对应
-
浏览器 48 版本以下内置驱动
3. 新建项目
4. 示例
4.1 打开 B 站
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.bilibili.com")
# 1、找到输入框,输入软件测试
driver.find_element(By.CLASS_NAME, "nav-search-input").send_keys("软件测试")
# 2、找到搜索框,点击搜索
driver.find_element(By.CLASS_NAME, "nav-search-btn").click()
time.sleep(3)
driver.close()
4.2 官方文档示例
from selenium import webdriver
from selenium.webdriver.common.by import By
# 八个基本组成部分
# 1. 使用驱动实例开启会话
driver = webdriver.Chrome()
# 2. 在浏览器上执行操作
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
# 3. 请求浏览器信息
title = driver.title
print(title)
# 4. 建立等待策略
driver.implicitly_wait(0.5)
# 5. 发送命令查找元素
textInput = driver.find_element(By.CSS_SELECTOR, "#my-text-id")
submitBtn = driver.find_element(By.XPATH, "//button[@type='submit']")
# 6. 操作元素
textInput.send_keys("hello")
submitBtn.click()
# 7. 获取元素信息
message = driver.find_element(By.ID, "message")
text = message.text
print(text)
# 8. 结束会话
driver.quit()
- 结合 pytest 后的代码
from selenium import webdriver
from selenium.webdriver.common.by import By
def setup():
# 1. 使用驱动实例开启会话
driver = webdriver.Chrome()
# 2. 在浏览器上执行操作
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
return driver
def teardown(driver):
# 8. 结束会话
driver.quit()
def test_eight_components():
driver = setup()
# 3. 请求浏览器信息
title = driver.title
assert title == "Web form"
# 4. 建立等待策略
driver.implicitly_wait(0.5)
# 5. 发送命令查找元素
textInput = driver.find_element(By.CSS_SELECTOR, "#my-text-id")
submitBtn = driver.find_element(By.XPATH, "//button[@type='submit']")
# 6. 操作元素
textInput.send_keys("hello")
submitBtn.click()
# 7. 获取元素信息
message = driver.find_element(By.ID, "message")
text = message.text
5. 关闭浏览器
-
driver.close()
:关闭单个浏览器窗口 -
driver.quit()
:关闭所有浏览器窗口 -
selenium4 会自动关闭
6. 八大元素定位
定位器 | 描述 |
---|---|
By.ID | 通过 id 定位 |
By.CLASS_NAME | 通过类名定位 |
By.TAG_NAME | 通过标签名定位 |
By.NAME | 通过 name 属性定位 |
By.LINK_TEXT | 根据链接中的文字定位 |
By.PARTIAL_LINK_TEXT | 根据链接部分名称查找定位,如果匹配多个元素,则只选择第一个元素 |
By.CSS_SELECTOR | 通过 CSS 选择器定位 |
By.XPATH | 通过 XPath 表达式定位 |
6.1 By.ID
- id 是唯一的,通过 id 查找元素时可以快速定位到目标元素
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
# 窗口最大化
driver.maximize_window()
driver.find_element(By.ID, "kw").send_keys("软件测试")
driver.find_element(By.ID, "su").click()
sleep(5)
6.2 By.CLASS_NAME
- 通过类名定位
driver = webdriver.Chrome()
driver.get("https:www.bilibili.com")
driver.maximize_window()
# 只获取class属性的第一个元素
# driver.find_element(By.CLASS_NAME, "nav-search-input").send_keys("软件测试")
# 使用find_elements()时,需要指定下标
driver.find_elements(By.CLASS_NAME, "nav-search-input")[0].send_keys("软件测试")
driver.find_element(By.CLASS_NAME, "nav-search-btn").click()
for ele in driver.find_elements(By.CLASS_NAME, 'channel-link'):
print(ele.txt)
time.sleep(3)
-
find_element()
:只获取相关属性的第一个元素 -
find_elements()
:获取相关属性的所有元素 -
当
class
中间有空格时,只能选取一个作为参数传递
6.3 By.TAG_NAME
- 通过标签名定位元素,不推荐使用
driver = webdriver.Chrome()
driver.get("https://www.bilibili.com")
driver.maximize_window()
driver.find_element(By.TAG_NAME, "input").send_keys("软件测试")
sleep(3)
6.4 By.NAME
- 通过 name 属性定位,可能不是唯一的,要根据实际情况进行判断
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
driver.maximize_window()
driver.find_element(By.NAME, "wd").send_keys("软件测试")
sleep(3)
6.5 By.LINK_TEXT
- 根据链接中的文字定位
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.baidu.com")
driver.find_element(By.LINK_TEXT, "新闻").click()
sleep(3)
6.6 By.PARTIAL_LINK_TEXT
- 根据链接中部分文字查找定位
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.baidu.com")
driver.find_element(By.PARTIAL_LINK_TEXT, "hao").click()
sleep(3)
6.7 By.CSS_SELECTOR
- CSS 常用汇总
选择器 | 样式 | 示例 | 示例说明 |
---|---|---|---|
标签选择器 | html 标签 | input | 选择所有 input 元素 |
ID 选择器 | #id 属性值 | #kw | 选择所有 id='kw' 的元素 |
类选择器 | .class 属性值 | .nav-search-input | 选择所有 class='nav-search-input' 的元素 |
属性选择器 | [属性名] | [name="wd"] | 选择所有 name="wd" 的元素 |
组合选择器 | 标签+属性表述 | input.s_ipt | 选择所有 class='ipt' 的 input 元素 |
父子关系 | 元素 1>元素 2 | div>a | 选择所有父级是 div 的 a 元素 |
后代关系 | 元素 1 元素 2 | div a | 选择 div 中的所有 a 元素 |
第一子元素 | :first-child | a:first-child | 选择所有 a 元素且该元素是其父级的第一个元素 |
最后一个元素 | :last-child | a:last-child | 选择所有 a 元素且该元素是其父级的最后一个元素 |
顺序选择器 | :nth-child(n) | a:nth-child(2) | 选择所有 a 元素且该元素是其父级的第二个子元素 |
- 示例
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.baidu.com")
# id
driver.find_element(By.CSS_SELECTOR, "#kw").send_keys("软件测试")
# name属性
driver.find_element(By.CSS_SELECTOR, "[name='wd']").send_keys("软件测试")
# type属性
driver.find_element(By.CSS_SELECTOR, "[type='submit']").click()
# class
driver.find_element(By.CSS_SELECTOR, ".s_btn").click()
# 标签属性
driver.find_element(By.CSS_SELECTOR, "a[href='https://www.hao123.com?src=from_pc_logon']").click()
# 模糊匹配-包含
driver.find_element(By.CSS_SELECTOR, "a[href*='image.baidu.com']").click()
# 模糊匹配-开头
driver.find_element(By.CSS_SELECTOR, "a[href^='https://image.baidu']").click()
# 模糊匹配-尾部
driver.find_element(By.CSS_SELECTOR, "a[href$='image.baidu.com/']").click()
# 组合定位
driver.find_element(By.CSS_SELECTOR, "input.s_ipt").send_keys("软件测试")
# 点击新闻
driver.find_element(By.CSS_SELECTOR, ".s-top-left-new.s-isindex-wrap>a").click()
driver.find_element(By.CSS_SELECTOR, "div#s-top-left a").click()
driver.find_element(By.CSS_SELECTOR, "#s-top-left a").click()
# 点击地图
driver.find_element(By.CSS_SELECTOR, "#s-top-left a:nth-child(3)").click()
driver.find_elements(By.CSS_SELECTOR, "#s-top-left a")[2].click()
# 第一个元素
driver.find_element(By.CSS_SELECTOR, "#s-top-left a:first-child").click()
# 最后一个元素
driver.find_element(By.CSS_SELECTOR, "#s-top-left a:last-child").click()
sleep(3)
6.8 By.XPATH
xpath 是一门在 XML 文档中查找信息的语言
XML 是可扩展标记语言,主要用于传输数据
为什么可以使用 xpath 定位 html?
XPath 是一种用于在 XML 文档中定位元素的语言
虽然 XML 和 HTML 是两种不同的标记语言,但 HTML 可以视为 XML 文档的一种特定形式
HTML 文档的结构和标签嵌套遵循 XML 的基本规则,因此 XPath 可以有效地在 HTML 文档中定位元素
XPath 定位 HTML 的基本原则是,将 HTML 文档视为 XML 文档,并使用 XPath 表达式来描述元素的位置关系
XPath 表达式可以使用标签名、属性、父子关系、索引等来选择特定的元素或元素集合
-
XPath 可以通过相对路径和绝对路径去定位元素,绝对路径从 HTML 根节点开始算,相对路径从任意节点开始
-
不推荐使用绝对路径,写起来太麻烦了
说明 | 举例 |
---|---|
从根节点开始选取(绝对路径) | /html/div/ |
从任意节点开始选取(相对路径) | //div,列举出所有 div 标签 |
选取当前节点的父节点 | //input/.. 会选取 input 的父节点 |
选取属性或根据属性选取 | |
使用 id 属性定位 | //div[@id='id_value'] |
使用 class 属性定位(有多个值时,需要全部填写) | //a[@class="mnav"] |
使用 name 属性定位 | //div[@name='wd'] |
多个属性定位 | //input[@name="wd" and @class="s_ipt"] |
第 n 个元素,使用 index 定位 | //div[@id="s-top-left"]/a[3] |
最后一个元素 | //a[@class="mnav"][last()] |
属性包含某字段 | //div[contains(@title, 'text')] |
属性以某字段开头 | //div[starts-with(@title, 'text')] |
属性以某字段结尾 | //div[ends-with(@title, 'text')] |
文本包含 | //a[contains(text(), "网盘")] |
文本等于 | //span[text() = "菜单"] |
同级弟弟元素 | //div[@id=='id']/following-sibling::div |
同级哥哥元素 | //div[@id=='id']/preceding-sibling::div |
- 示例 1:
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.baidu.com")
# 绝对路径
driver.find_element(By.XPATH, '/html/body/div/div/div[3]/a').click()
# 根据ID定位
driver.find_element(By.XPATH, '//input[@id="kw"]').send_keys("selenium")
driver.find_element(By.XPATH, '//input[@id="su"]').click()
# 根据class定位
driver.find_element(By.XPATH, '//input[@class="s_ipt"]').send_keys("selenium")
# 根据name定位
driver.find_element(By.XPATH, '//input[@name="wd"]').send_keys("selenium")
# 多个属性组合定位
driver.find_element(By.XPATH, '//input[@name="wd" and @class="s_ipt"]').send_keys("selenium")
# 多数据使用下标定位
driver.find_element(By.XPATH, '//div[@id="s-top-left"]/a[4]').click()
# 通过子元素找父元素
driver.find_element(By.XPATH, '//div[@id="s-top-left"]/../div[3]/a[1]').click()
# 根据span文本内容定位
driver.find_element(By.XPATH, '//span[text() = "“退钱哥”公开应聘足球社会监督员"]').click()
# 根据文本包含
driver.find_element(By.XPATH, '//span[contains(text(), "退钱哥")]').click()
sleep(10)
- 示例 2:
driver = webdriver.Chrome()
driver.maximize_window()
# driver.get("https://element.eleme.cn/#/zh-CN/component/cascader")
driver.get("https://baidu.com")
# 同级弟弟
# driver.find_element(By.XPATH, "//span[contains(text(),'默认 click 触发子菜单')]/following-sibling::div/div/input").click()
# 同级哥哥(1是离最近的哥哥,数字越大,就离越远)
driver.find_element(By.XPATH, "//a[text()='贴吧']/preceding-sibling::a[1]").click()
sleep(3)
7. Web 元素
7.1 交互
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.selenium.dev/selenium/web/inputs.html")
# 清空元素内容
driver.find_element(By.NAME, "email_input").clear()
# 输入内容
driver.find_element(By.NAME, "email_input").send_keys("admin@localhost.dev")
# 点击
driver.find_element(By.NAME, "color_input").click()
7.2 关于元素的信息
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.selenium.dev/selenium/web/inputs.html")
# 是否显示
is_email_visible = driver.find_element(By.NAME, "email_input").is_displayed()
# 是否启用
value = driver.find_element(By.NAME, 'button_input').is_enabled()
# 是否被选定
value2 = driver.find_element(By.NAME, "checkbox_input").is_selected()
# 获取特性或属性
email_txt = driver.find_element(By.NAME, "email_input")
value_info = email_txt.get_attribute("value")
# 获取文本内容
driver.get("https://www.selenium.dev/selenium/web/linked_image.html")
text = driver.find_element(By.ID, "justanotherLink").text
print(is_email_visible)
print(value)
print(value2)
print(value_info)
print(text)
driver.quit()
8. 浏览器交互
8.1 获取浏览器信息
title = driver.title # 获取浏览器标题
url = driver.current_url # 获取当前url
8.2 导航
driver.forward() # 前进
driver.back() # 后退
driver.refresh() # 刷新
8.3 弹框
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
def setup():
driver = webdriver.Chrome()
driver.get("https://www.selenium.dev/documentation/webdriver/interactions/alerts/")
return driver
def teardown(driver):
driver.quit()
def test_alert():
driver = setup()
element = driver.find_element(By.LINK_TEXT, "See an example alert")
element.click()
sleep(3)
wait = WebDriverWait(driver, timeout=2)
alert = wait.until(lambda d: d.switch_to.alert)
text = alert.text
alert.accept()
assert text == "Sample alert"
def test_confirm():
driver = setup()
element = driver.find_element(By.LINK_TEXT, "See a sample confirm")
driver.execute_script("arguments[0].click();", element)
sleep(3)
wait = WebDriverWait(driver, timeout=2)
alert = wait.until(lambda d: d.switch_to.alert)
text = alert.text
alert.dismiss()
assert text == "Are you sure?"
def test_prompt():
driver = setup()
element = driver.find_element(By.LINK_TEXT, "See a sample prompt")
driver.execute_script("arguments[0].click();", element)
wait = WebDriverWait(driver, timeout=2)
alert = wait.until(lambda d: d.switch_to.alert)
alert.send_keys("Selenium")
text = alert.text
alert.accept()
assert text == "What is your tool of choice?"
-
其中
driver.execute_script("arguments[0].click();", element)
-
driver.execute_script()
:用于在当前页面上执行 JavaScript 代码 -
"arguments[0].click();"
:传递给execute_script()
的 JavaScript 代码。arguments[0]
是一个占位符,表示传递给execute_script()
方法的第一个参数 -
element
:作为 JavaScript 中arguments[0]
的值传入
-
-
相当于:
element.click();
9. 定位实战
9.1 单选框 radio 定位
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.iviewui.com/view-ui-plus/component/form/radio")
# 根据按钮点击
driver.find_elements(By.XPATH, "//input[@class='ivu-radio-input' and @type='radio']")[1].click()
sleep(1)
driver.find_elements(By.XPATH, "//input[@class='ivu-radio-input' and @type='radio']")[2].click()
sleep(1)
driver.find_elements(By.XPATH, "//input[@class='ivu-radio-input' and @type='radio']")[3].click()
sleep(2)
# 点击文本
driver.find_element(By.XPATH, "//span[text()='Android']").click()
sleep(1)
driver.find_element(By.XPATH, "//span[text()='Windows']").click()
sleep(1)
driver.find_element(By.XPATH, "//span[text()='Android']/preceding-sibling::span/input").click()
sleep(1)
driver.close()
9.2 CheckBox 多选框定位
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.iviewui.com/view-ui-plus/component/form/checkbox")
# driver.find_element(By.XPATH, "//span[text()='香蕉']").click()
# sleep(1)
# driver.find_element(By.XPATH, "//span[text()='苹果']").click()
# sleep(1)
# driver.find_element(By.XPATH, "//span[text()='西瓜']").click()
# sleep(1)
driver.find_element(By.XPATH, "//span[text()='香蕉']/preceding-sibling::span/input").click()
sleep(1)
driver.find_element(By.XPATH, "//span[text()='苹果']/preceding-sibling::span/input").click()
sleep(1)
driver.find_element(By.XPATH, "//span[text()='西瓜']/preceding-sibling::span/input").click()
sleep(1)
driver.close()
9.3 Select 下拉框定位
- Select 下拉框
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://sahitest.com/demo/selectTest.htm")
se = Select(driver.find_element(By.ID, "s1"))
sleep(1)
# 根据index下标获取,从0开始
se.select_by_index(1)
sleep(2)
# 根据option的value进行选择
se.select_by_value("50")
sleep(2)
# 根据option的文本内容进行选择
se.select_by_visible_text("Cell Phone")
sleep(2)
driver.close()
- 非 Select 下拉框
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.iviewui.com/view-ui-plus/component/form/select")
driver.find_element(By.XPATH, "//div[@tabindex='0']").click()
sleep(2)
driver.find_elements(By.XPATH, "//div[@tabindex='0']/following-sibling::div/ul[2]/li")[1].click()
sleep(2)
driver.close()
9.4 联级选择器定位
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.iviewui.com/view-ui-plus/component/form/cascader")
driver.find_element(By.XPATH, "//input[@placeholder='请选择']").click()
sleep(1)
driver.find_element(By.XPATH, "//li[contains(text(), '北京')]").click()
sleep(1)
driver.find_element(By.XPATH, "//li[contains(text(), '故宫')]").click()
sleep(1)
driver.close()
9.5 日期选择器定位
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://www.iviewui.com/view-ui-plus/component/form/date-picker")
driver.find_element(By.XPATH, "//input[@class='ivu-input ivu-input-default ivu-input-with-suffix']").send_keys("2024-08-15")
sleep(1)
driver.find_elements(By.XPATH, "//input[@class='ivu-input ivu-input-default ivu-input-with-suffix']")[1].send_keys("2024-08-27 - 2024-09-30")
sleep(1)
driver.close()
9.6 弹框(Alert、confirm、prompt)
- alert
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://sahitest.com/demo/alertTest.htm")
driver.find_element(By.NAME, "b1").click()
# 使用alert.text获取弹框文本
print(driver.switch_to.alert.text)
sleep(1)
# 点击确定
driver.switch_to.alert.accept()
driver.close()
- confirm
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://sahitest.com/demo/confirmTest.htm")
driver.find_element(By.NAME, "b1").click()
# 获取弹框文本
print(driver.switch_to.alert.text)
sleep(1)
# 点击确定
driver.switch_to.alert.accept()
# 点击取消
driver.switch_to.alert.dismiss()
sleep(1)
driver.close()
- prompt
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://sahitest.com/demo/promptTest.htm")
driver.find_element(By.NAME, "b1").click()
# 输入文字
driver.switch_to.alert.send_keys("测试alert")
sleep(1)
# 点击确定
driver.switch_to.alert.accept()
# 点击取消
driver.switch_to.alert.dismiss()
sleep(1)
driver.close()
9.7 文件上传 upload
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://sahitest.com/demo/php/fileUpload.htm")
upload = driver.find_element(By.ID, "file")
upload.send_keys(r"E:\Code\PythonProjects\test\selenium\file\xx.jpg")
sleep(1)
driver.find_element(By.NAME, "submit").click()
sleep(1)
driver.close()
9.8 获取具有焦点的 DOM 元素
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.google.com")
driver.find_element(By.CSS_SELECTOR, '[name="q"]').send_keys("webElement")
# 获取具有焦点的 DOM 元素
attr = driver.switch_to.active_element.get_attribute("title")
print(attr)