Python Web 自动化测试入门实战
Python Web 自动化测试入门实战
自动化入门介绍
自动化测试概念
自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。其实质是写脚本或使用工具测试程序。
自动化测试分类
自动化测试是使用各种工具或程序代替人工测试的一种行为。只要是解除人工操作的测试都可以称为自动化测试,细分又有好多种类,下面简单列举一些。
- Web UI 自动化测试。
- API 自动化测试。
- 性能自动化测试。
- 单元测试。
- Windows 窗体 UI 自动化测试。
本系列实验以 Python Web UI 自动化测试为主,为了方便起见以后直接简称自动化测试。
怎么学习自动化测试
写好自动化测试的前提是有夯实的手工测试基础,学习自动化测试的基本路线大致如下:
- 做好手工功能测试(包括测试理论知识,涉及的工具使用等)。
- 学习前端基本知识(HTML、CSS、JavaScript)。
- 掌握一门编程语言(例如 Python、Java)。
- 精通自动化测试工具(目前最流行的为 Selenium)。
- 精通自动化测试框架(UnitTest、Pytest)。
- 熟悉自动化测试模型(线性模型、模块化驱动、数据驱动、关键字驱动、行为驱动)。
- 熟悉集成工具(例如 Jenkins)。
学完以上七点,便可在项目中进行自动化测试。
本系列实验将会以 Python3 + Selenium 为基础进行展开。
什么样的项目适合自动化测试
对项目进行自动化测试之前要了解项目是否适合做自动化测试。业界普遍从三个方面进行考虑。
- 需求稳定,变更不会太频繁。
自动化测试只适用比较稳定的系统或系统中的部分功能模块。如果需求变更过于频繁则自动化测试的脚步维护也就比较大,进而成本也会加大,从而消耗更多的资源,是不划算的。
- 维护周期长,具有生命力。
开发自动化测试脚本是需要时间的。如果项目周期比较短,下一个版本已经开始了,对某些模块有了新的变动,而正在进行的自动化测试开发将会变得毫无意义。
- 被测系统开发规范,可测性强。
测试脚本的开发需要根据被测系统而考虑,如果被测系统架构不完善则测试工具和测试技术很难应对,一旦测试人员的能力不是很好,则设计出来的测试框架、写出来的测试脚本在应对性上也很差。由此便造成自动测试产生的价值没有手工测试来的直接。
selenium 是什么
Selenium 是一个用于 Web 系统自动化测试的工具集,现在所说的 Selenium 通常是指 Selenium Suite,其包含 Selenium IDE、Selenium WebDriver 和 Selenium Grid 三部分。
- Selenium IDE:是一个 Firefox 插件,可以根据用户的基本操作自动录制脚本,然后在浏览器中进行回放。
- Selenium WebDriver:WebDriver 的前身是 Selenium RC,其可以直接给浏览器发送命令模拟用户的操作。Selenium RC 为 WebDriver 的核心部分,它可以使用编程语言如 Java、C#、PHP、Python、Ruby 和 Perld 的强大功能来创建更复杂的测试。Selenium RC 分为 ClientLibraries(编写测试脚本)和 Selenium Server(控制浏览器行为)两部分。
- Selenium Grid:是一个用于运行在不同的机器、不同的浏览器并行测试的工具,用于加快测试用例的运行速度。
安装浏览器驱动
WebDriver 是 Selenium Tool 套件中最重要的组件,其就像一个媒介,用脚本驱动 WebDriver,WebDriver 再去控制浏览器,从而实现脚本对浏览器的操作。
只有安装了浏览器驱动才能使用 Selenium 发送指令模拟人类行为操作浏览器。不同的浏览器需要安装各自的驱动,接下来以 Chrome 浏览器为例安装 chromedriver.exe。
- 查看 Chrome 版本。
由于安装的 chromedriver.exe 版本需要和 Chrome 浏览器版本匹配,所以我们需要知道 Chrome 的版本。从 Chrome 浏览器右上角的菜单中依次选择【帮助(E)】、【关于 Google Chrome(G)】,查看浏览器的版本。
- 下载 chromedriver。
进入 chromedriver 下载地址下载 https://chromedriver.storage.googleapis.com/index.html 或使用淘宝镜像地址下载 https://npm.taobao.org/mirrors/chromedriver/ ,进入后选择对应的版本号。
然后根据自己的系统选择对应的 zip 文件进行下载。如实验的系统是 Linux 则下载 chromedriver_linux64.zip,并将其保存在 /home/shiyanlou/Code/ 下。
下载后通过 cd 命令进入到 /home/shiyanlou/Code/ 目录中,使用命令 unzip chromedriver_linux64.zip 进行解压。
- 将 chromedriver 移至 python 所在目录下。
将 chromedriver 移动至 Python 编辑器所在的目录 /usr/bin/ 下,使 chromedriver 与 python 处于同一目录下,这样做的目的是便于 Python 在执行时可以找到 chromedriver。 使用命令 sudo cp -r /home/shiyanlou/Code/chromedriver /usr/bin/。
不同的浏览器需要是使用不同的驱动,下面列出 FireFox 和 IE 浏览器驱动的下载地址。
- Firefox 浏览器驱动的下载网址:https://github.com/mozilla/geckodriver/releases/ 。
- IE 浏览器驱动下载:http://www.nuget.org/packages/Selenium.WebDriver.IEDriver/ 。
开发第一个自动化测试脚本
sudo apt install python3-pip sudo pip3 install --upgrade pip pip3 install selenium
继续输入下面命令,进入到 python 环境中,然后导入 webdriver。
python3 from selenium import webdriver
输入 driver = webdriver.Chrome() 启动 Chrome 浏览器。
输入 driver.get("https://www.lanqiao.cn/") 后在浏览器中打开蓝桥首页。
输入 driver.close() 关闭浏览器。
在命令行中体验了怎么运行,现在将上面的步骤写成 py 文件。
在 /home/shiyanlou/Code/ 目录下新建 myfirst_project.py 文件。
在 myfirst_project.py 中编辑代码,操作浏览器打开蓝桥首页,然后关闭浏览器。
from selenium import webdriver driver = webdriver.Chrome() driver.get("https://www.lanqiao.cn/") driver.close()
然后打开 Xfce 终端,输入下面命令运行 myfirst_project.py 脚本。
python3 /home/shiyanlou/Code/myfirst_project.py命令执行后脚本会驱动 webdriver 打开 Chrome 浏览器,然后访问蓝桥首页,最后关闭浏览器。
元素定位
元素定位是在进行自动化测试时的基本功,本次实验将介绍元素的八大定位、By 方法定位、定位一组元素和怎么确认定位到的元素就是自己需要的。
知识点
- id 定位
- class 定位
- name 定位
- tag 定位
- xPath 定位
- link 定位
- Partial link 定位
- CSS 定位
- By 定位
- 确认元素的唯一性
- 定位一组元素
其他环境准备
Chrome 浏览器对应版本 chromedriver 已经存放在 /usr/bin/ 目录下,无需再次进行操作。
元素定位
元素定位是通过在 HTML 代码中查找一些元素属性确定需要的元素。
在进行本次实验之前需要先进行下面的操作,在接下来的实验中将会在此基础上进行。
打开 Xfce 终端,依次输入下面语句,进入到 Python 环境,并且导入 webdriver,然后启动 Chrome 浏览器,并且访问元素定位练习的页面。
python3 from selenium import webdriver driver = webdriver.Chrome() driver.get("https://labfile.oss.aliyuncs.com/courses/2335/20.html")
实验中用到的方法
- 获取元素属性值: .get_attribute('attribute') 。
- 断言两个值相等: assert 预期结果 == 实际结果 。
- 获取文本内容: .text 。
id 定位
语法: find_element_by_id('id') 。
定位元素:通过元素 id 属性定位输入框。
Xfce 终端中输入: driver.find_element_by_id('search')。
断言:通过元素其他属性断言定位的元素正确性。
通过检索框的 maxlength 属性值进行断言,如果获取到的 maxlength 值 与 HTML 脚本中的 maxlength='10' 相等,则表示定位的元素正确。
获取 maxlength 属性值: id_maxlength = driver.find_element_by_id('search').get_attribute('maxlength')。
通过 maxlength 属性值进行断言: assert id_maxlength == '10'。
如果输入断言后出现 Error 错误则获取到的元素错误,反之定位成功。
class 定位
语法:find_element_by_class_name('class')。
定位元素:通过元素 class 属性定位检索按钮。
Xfce 终端中输入: driver.find_element_by_class_name('btn-search')。
断言:通过元素其他属性断言定位的元素正确性。
通过检索按钮的 value 属性值进行断言,如果获取到的 value 值与 HTML 脚本中的 value="检索" 相等,则表示定位的元素正确。
获取 value 属性值: class_value = driver.find_element_by_class_name('btn-search').get_attribute('value')。
通过 value 属性值进行断言: assert class_value == "检索"。
如果输入断言后出现 Error 错误则获取到的元素错误,反之定位成功。
name 定位
语法: find_element_by_name('name')。
定位元素:通过元素 name 属性定位复选框。
Xfce 终端中输入: driver.find_element_by_name('language')。
断言:通过元素其他属性断言定位的元素正确性。
通过复选框的 type 属性值进行断言,如果获取到的 type 值与 HTML 脚本中的 type="checkbox" 相等,则表示定位的元素正确。
获取 type 属性值: name_type = driver.find_element_by_name('language').get_attribute('type')。
通过 type 属性值进行断言: assert name_type == "checkbox"。
如果输入断言后出现 Error 错误则获取到的元素错误,反之定位成功。
tag 定位
语法:find_element_by_tag_name('tag')。
定位元素:通过元素 tag 属性定位文本 “tag 定位”。
Xfce 终端中输入: driver.find_element_by_tag_name('h4')。
断言:通过元素其他属性断言定位的元素正确性。
通过文本的内容进行断言,如果获取到的文本内容与 HTML 脚本中的文本内容 “tag 定位” 相等,则表示定位的元素正确。
获取文本内容: tag_text = driver.find_element_by_tag_name('h4').text。
通过文本内容进行断言: assert tag_text == "tag定位"。
如果输入断言后出现 Error 错误则获取到的元素错误,反之定位成功。
xPath 定位
语法: find_element_by_xpath('xPath')。
定位元素:通过 xPath 定位文本 “xPath 定位”。
通过 HTML 脚本得到路径: /html/body/div/p。
Xfce 终端中输入:driver.find_element_by_xpath('/html/body/div/p')。
断言:通过元素其他属性断言定位的元素正确性。
通过文本的内容进行断言,如果获取到的文本内容与 HTML 脚本中的文本内容 “xPath 定位” 相等,则表示定位的元素正确。
获取文本内容: xPath_text = driver.find_element_by_xpath('/html/body/div/p').text。
通过文本内容进行断言: assert xPath_text == "xPath定位"。
如果输入断言后出现 Error 错误则获取到的元素错误,反正定位成功。
xPath 定位有两种选择路径的方式,绝对路径定位和相对路径定位:
- 绝对路径定位:从页面的最初位置开始定位,以一个单斜杠 “/” 开头,例如 /html/body/div/p。
- 相对路径定位:从页面中可以确定唯一性的一个节点开始定位,以双斜杠 “//” 开头,例如 //div[@id='search']/input,定位的元素是在含有 id='search' 属性的 div 节点下的 input 元素。
xPath 是一种比较实用的定位方式,通过 xPath 定位一个元素可以有多种方式实现。
定位方式 | 含义 | 示例 |
---|---|---|
* | 匹配任何元素节点 | //*[@name='firstName'] |
@ | 属性匹配 | //input[@name='firstName'] |
[index] | 索引定位 | //ul/li[4] |
[contains(@属性, '属性值')] | 模糊匹配 | /a[contains(@href, 'news')] |
属性 1 and 属性 2 | 多个属性匹配 | //input[@type='button' and @name='ok'] |
starts-with(@属性, '属性值') | 属性以某字段开头 | //input[starts-with(@name, 'first')] |
ends-with (@属性, '属性值') | 属性以某字段结尾 | //input[ends-with(@name, 'name')] |
.. | 选取元素的父节点 | //input[@name='firstName']/.. |
preceding-sibling | 选取同级的哥哥元素 | //ul/li[2]/preceding-sibling:: * |
following-sibling | 选取同级的弟弟元素 | //ul/li[2]/following-sibling:: * |
link 定位
语法: find_element_by_link_text('text')。
定位元素:通过元素 link 定位文本 “Tynam”。
Xfce 终端中输入: driver.find_element_by_link_text('Tynam')。
断言:通过元素其他属性断言定位的元素正确性。
通过 a 链接的 href 属性值进行断言,如果获取到的 href 值与 HTML 脚本中的 href="http://tynam.com/" 相等,则表示定位的元素正确。
获取 href 属性值:link_href = driver.find_element_by_link_text('Tynam').get_attribute('href')。
通过 href 属性值进行断言: assert link_href == "http://tynam.com/"。
如果输入断言后出现 Error 错误则获取到的元素错误,反之定位成功。
Partial link 定位
语法:find_element_by_partial_link_text('partialText')。
定位元素:通过元素 Partial link 定位文本 “Partial link 定位” 。
Xfce 终端中输入: driver.find_element_by_partial_link_text('link定位')。
断言:通过元素其他属性断言定位的元素正确性。
通过文本的内容进行断言,如果获取到的文本内容与 HTML 脚本中的文本内容 “Partial link 定位” 相等,则表示定位的元素正确。
获取文本内容: partial_link_text = driver.find_element_by_partial_link_text('link定位').text。
通过文本内容进行断言: assert partial_link_text == "Partial link定位"。
如果输入断言后出现 Error 错误则获取到的元素错误,反之定位成功。
CSS 定位
语法: find_element_by_css_selector('css') 。
定位元素:通过 CSS 定位 "CSS 定位" 按钮 。
通过 HTML 脚本可知,button 标签中有一个 class="css" 属性,则 CSS 可写成 button.css。
Xfce 终端中输入 driver.find_element_by_css_selector('button.css')。
断言:通过元素其他属性断言定位的元素正确性。
通过按钮文本内容进行断言,如果获取到的文本内容与 HTML 脚本中的文本内容 “CSS 定位” 相等,则表示定位的元素正确。
获取文本内容: css_text = driver.find_element_by_css_selector('button.css').text。
通过文本内容进行断言: assert css_text == "CSS定位"。
如果输入断言后出现 Error 错误则获取到的元素错误,反之定位成功。
CSS 是一种非常灵活的定位方式,和 xPath 一样,定位一个元素可以有多种方式实现。
定位方式 | 含义 | 示例 |
---|---|---|
* | 通配符 | * |
#id | id 选择 | #login |
.class | class 选择 | div.active |
Element | 标签选择 | input |
element1, element2 | 匹配 element1 和 element2 | input, p |
element1>element2 | 匹配 element1 下子元素为 element2 的元素 | div>.active |
element1+element2 | 匹配与 element1 同级并且在其相邻后面的 element2 元素 | .active+li |
[attribute=value] | 匹配属性 attribute 的值为 value 的元素 | input[type="button"] |
:first-child | 选择第一个子元素 | ul:first-child |
element:not(s) | 匹配 element 元素但元素中没有 s 值 | div:not(.active) |
By 定位
By 定位和使用 webdriver.find_element_by_xxx 的定位方式类似,只是在写法上有些区别。使用 By 方式定位时需要导入 By 类: from selenium.webdriver.common.by import By。
定位方式 | 定位单个元素 | 示例 |
---|---|---|
id 定位 | find_element(By.ID, "id") | find_element(By.ID, "search") |
class 定位 | find_element(By.CLASS_NAME, "class") | find_element(By.CLASS_NAME, "btn-search") |
name 定位 | find_element(By.NAME, "name") | find_element(By.NAME," language") |
tag 定位 | find_element(By.TAG_NAME, "tag") | find_element(By.TAG_NAME, "h4") |
xPath 定位 | find_element(By.XPATH, "xPath") | find_element(By.XPATH, "/html/body/di*** |
link 定位 | find_element(By.LINK_TEXT, "text") | find_element(By.LINK_TEXT, "Tynam") |
Partial link 定位 | find_element(By.PARTIAL_LINK_TEXT,"partialText") | find_element(By.PARTIAL_LINK_TEXT, "link 定位") |
CSS 选择器定位 | find_element(By.CSS_SELECTOR, "css") | find_element(By.CSS_SELECTOR, "button.css") |
确认元素的唯一性
在本次实验中,确定定位到的元素就是所需的,通过添加 assert 断言进行确认。如果在定位时就确认写的语法定位到的元素就是唯一的,那么将会省去甚多麻烦。下面介绍一些直接就可以确定定位到的元素是所需的两种方法。
1.源码中检索确认。开启查看元素,在源码中检索。打开检索快捷键:Ctrl + F (Windows 下)/ Command + F (Mac OS X 系统下)。
输入要查找的元素属性,例如查找 button 标签中 class=“css” 的元素,则在检索框中输入 button.css。
在检索框的右侧可以看到匹配到的元素数量。如果剔除 CSS、JavaScript 中的记录后数量为 1,则基本可以确定定位的元素就是需要的元素。
2.控制台中确认。主要是使用 JavaScript 中定位元素的方法,如果项目中使用了 JQuery 也可借助其语法进行确认。开启查看元素,进入控制台(Console),语法和 WebDriver API 中定位元素语法类似。例如确认 id=“search” 则输入 document.getElementById("search")。
属性在控制台中确认元素的语法:
- id 属性确认:document.getElementById()。
- class 属性确认:document.getElementsByClassName()。
- name 属性确认:document.getElementsByName()。
- tag 属性确认:document.getElementsByTagName()。
- xPath 确认:document.getSelection()。
- CSS 确认:document.querySelector()。
定位一组元素
find_elements 和 find_element 定位方式是一致的,只不过 find_elements 返回的是一组数据,且以列表数据结构形式返回,而 find_element 返回的只有一条数据。同样的,定义一组元素也有两种方式,即 find_elements_by_xxx 方法和 find_elements(By.xxx, " ") 方法。
- id 定位:find_elements_by_id()。
- class 定位:find_elements_by_class_name()。
- name 定位:find_elements_by_name()。
- tag 定位:find_elements_by_tag_name()。
- xPath 定位:find_elements_by_xpath()。
- link 定位:find_elements_by_link_text()。
- Partial link 定位:find_elements_by_partial_link_text()。
- CSS 定位:find_elements_by_css_selector()。
定位元素:定位页面中所有 tag=“input” 的元素。
Xfce 终端中输入:tags_input = driver.find_elements_by_tag_name('input')。
将其结果进行打印:print(tags_input)。
通过本次实验,对元素定位有一个基本的理解,在工作中比较通用的定位方式是 xPath 和 CSS 定位,因为这两种定位方式比较灵活,可以结合 id、name、tag、link、Partial link 的定位一起使用,在以后工作中,需要多加练习才能在运用时游刃有余。
浏览器操作
本次实验主要介绍通过 Selenium 对浏览器的一些行为操作,例如浏览器最大化、页面刷新等。
知识点
- 浏览器最大化
- 设置浏览器大小
- 访问网页
- 浏览器前进和后退
- 页面刷新
- 关闭窗口
- 退出浏览器
- 获取页面 title
- 获取页面 url
- 获取页面源码
- 切换窗口
- 操作滚动条
其他环境准备
Chrome 浏览器对应版本 chromedriver 已经存放在 /usr/bin/ 目录下,无需再次进行操作。
浏览器操作
自动化测试中通过 Selenium 的一些方法,达到模拟人的动作对浏览器的行为进行操控。
在进行本次实验之前需要先进行下面的操作,在接下来的实验中将会在此基础上进行。
打开 Xfce 终端,依次输入下面语句,进入到 Python 环境,并且导入 webdriver,然后启动 Chrome 浏览器。
python3 from selenium import webdriver driver = webdriver.Chrome()
浏览器最大化
浏览器最大化是将浏览器最大化占据整个屏幕。
语法:.maximize_window() 。
操作:Xfce 终端中输入 driver.maximize_window()。
结果:浏览器最大化并且占据整个屏幕。
设置浏览器大小
可以自定义浏览器的宽和高,浏览器默认打开大小为 400 像素(宽) X 580 像素(高)。
语法: .set_window_size(宽,高)。
操作:将浏览器的宽和高分别设置为 600 像素、 700 像素。
Xfce 终端中输入 driver.set_window_size(600, 700)。
结果:浏览器在屏幕中显示的大小为 600 像素(宽) X 700 像素(高)。
访问网页
访问网页就是输入 URL 后打开一个页面。
语法: .get()。
操作:访问蓝桥首页,URL 为 https://www.lanqiao.cn/ 。
Xfce 终端中输入 driver.get("https://www.lanqiao.cn/")。
结果:浏览器打开了蓝桥网站首页。
浏览器后退
浏览器后退是返回上一个网页。
语法: .back()。
操作:在上一个访问网页操作(访问蓝桥首页)后,返回打开浏览器时的默认页面。
Xfce 终端中输入 driver.back()。
结果:浏览器返回到打开浏览器时的默认页面。
浏览器前进
浏览器前进与浏览器返回表现相反,在浏览器返回后通过浏览器前进操作可以继续访问浏览器返回动作之前的网页。
语法: .forward()。
操作:在上一个浏览器后退操作(返回打开浏览器时的默认页面)后,使用浏览器前进功能再次访问蓝桥网站首页。
Xfce 终端中输入 driver.forward()。
结果:浏览器再次进入到蓝桥网站首页。
页面刷新
页面刷新是方法获取最新的数据。
语法: .refresh()。
操作:在上一个浏览器前进操作(再次进入到蓝桥网站首页)后,对网页进行刷新。
Xfce 终端中输入 driver.refresh()。
结果:网页进行了重新渲染。
关闭窗口
语法: .close()。
如果浏览器中打开了多个窗口,使用 close() 关闭的是当前窗口;如果只有一个窗口,使用 close() 则是关闭浏览器。
操作:在上一个页面刷新操作(刷新蓝桥网站首页页面)后,依次进行如下操作。
-
Xfce 终端中如下语句,打开国信蓝桥——1+X 证书制度试点页面 driver.find_element_by_css_selector("#nav-collapse .nav-link:first-child").click()。
-
使用 close() 方法关闭当前窗口 driver.close()。
结果:浏览器关闭了蓝桥网站首页窗口。
提示:在打开国信蓝桥——1+X 证书制度试点页面后,浏览器上显示的是国信蓝桥——1+X 证书制度试点页面,但关闭的却是蓝桥网站首页窗口。这是因为打开新的窗口后,没有进行窗口的切换,selenium 还停留在蓝桥网站首页窗口,只不过所看到的是国信蓝桥——1+X 证书制度试点窗口而已,接下来的操作还是针对蓝桥网站首页窗口进行操作。在做完本次实验 “切换窗口” 小实验后相信会对浏览器窗口操作有一个更深的认识。
退出浏览器
语法: .quit()。
如果浏览器有多个窗口,使用 quit() 方法则关闭所有的窗口并且退出浏览器。
操作:关闭打开的浏览器。
Xfce 终端中输入 driver.quit()。
结果:浏览器退出。
获取页面 title
Title 指的是 HTML 脚本 head 中的 title 值。
语法: .title。
操作:依次输入下面语句访问蓝桥网站首页。
driver = webdriver.Chrome() driver.get("https://www.lanqiao.cn/")
获取页面 title 并且打印 print(driver.title)。
打印结果:连接高校和企业 - 蓝桥。
获取页面 url
获取当前页面的 URL。
语法: .current_url。
操作:获取当前页面 URL 并且打印 print(driver.current_url)。
获取页面源码
获取当前页面源代码。
语法: .page_source。
操作:获取当前页面源码并且打印 print(driver.page_source)。
为了接下来的实验进行时更清楚观察,暂时退出浏览器 driver.quit()。
打印结果:
切换窗口
当浏览器打开多个窗口,要对当前窗口以外的窗口进行操作时,就需要先切换到目标窗口才可以进行操作。确定具体窗口需要使用窗口句柄定位,然后进行切换。
语法:
- 获得当前窗口句柄 .current_window_handle。
- 获取所有的窗口句柄 .window_handles。
操作:根据下面语句访问蓝桥网站首页,然后进行窗口切换操作。
- driver = webdriver.Chrome()。
- driver.get("https://www.lanqiao.cn/")。
- 打开国信蓝桥——1+X 证书制度试点页面 driver.find_element_by_css_selector("#nav-collapse .nav-link:first-child").click()。
- 打印当前页面的 title print(driver.title)。
打印结果:连接高校和企业 - 蓝桥。
5. 打印当前窗口句柄 print(driver.current_window_handle)。打印结果:CDwindow-21F04009ED58ABA8DBF0B4EA4F67C779。
6. 打印所有窗口句柄 print(driver.window_handles)。打印结果:['CDwindow-21F04009ED58ABA8DBF0B4EA4F67C779', 'CDwindow-FBBB5A65E5B4AB0D7ECC8F00385274AC']。
7. 由结果可知,所有句柄返回的是一个列表,当前窗口句柄为第一个值,根据列表索引通过句柄进行窗口切换 driver.switch_to.window(driver.window_handles[1])。再次打印当前页面的 title print(driver.title)。
打印结果:国信蓝桥——1+X 证书制度试点页面。
由打印的页面 title 可知,窗口切换成功。
8. 退出浏览器 driver.quit()。操作滚动条
当页面上的某些元素不在浏览器的可见视野中,如果要操作就需要滑动滚动条使元素可见,而滚动条的操作需要借助 JavaScript 完成。
语法:
- 移动到页面顶部: .execute_script("window.scrollTo(document.body.scrollHeight,0)")。
- 移动到页面底部: .execute_script("window.scrollTo(0,document.body.scrollHeight)")。
- 移动到使元素顶部与窗口的顶部对齐位置: .execute_script("arguments[0].scrollIntoView();", element)。
- 移动到使元素底部与窗口的底部对齐位置: .execute_script("arguments[0].scrollIntoView(false);", element) 。
操作:根据下面语句访问蓝桥网站首页,然后操作进行滚动条操作。
- driver = webdriver.Chrome()。
- driver.get("https://www.lanqiao.cn/")。
- 移动到页面底部 driver.execute_script("window.scrollTo(0,document.body.scrollHeight)")。
结果:滚动条滑动到页面底部。
4. 移动到页面顶部 driver.execute_script("window.scrollTo(document.body.scrollHeight,0)")。结果:滚动条滑动到页面顶部。
5. 移动到使轮播图左侧的课程菜单顶部与窗口的顶部对齐位置。通过 class 属性获取轮播图左侧的课程菜单 element = driver.find_element_by_class_name('course-nav')。
操作滚动条 driver.execute_script("arguments[0].scrollIntoView();", element)。
结果:轮播图左侧的课程菜单顶部与窗口的顶部对齐。
6. 移动到使页面 footer 中的微信标识底部与窗口的底部对齐位置。通过 class 属性获取 footer 中的微信标识元素 element = driver.find_element_by_class_name('weixin')。
操作滚动条 driver.execute_script("arguments[0].scrollIntoView(false);", element)。
结果: footer 中的微信标识底部与窗口的底部对齐。
通过本次实验操作,对浏览器的行为操作有一个基本掌握。做完本次实验,不要只满足当前实验中的练习,在做完本次实验后对相关知识点要勤加练习才是。对象操作
本次实验主要是对页面中元素对象操作的一些方法进行实战。例如鼠标左键单击、内容输入、获取元素的属性值、元素的状态判断。
知识点
- 单击元素
- 输入内容
- 清空内容
- 获取属性值
- 获取文本内容
- 对象显示状态判断
- 对象编辑状态判断
- 对象选择状态判断
其他环境准备
Chrome 浏览器对应版本 chromedriver 已经存放在 /usr/bin/ 目录下,无需再次进行操作。
对象操作
网页中的元素对象,由于属性不同而表现的行为就不同,操作也就不同。下面针对元素对象操作的一些方法进行实战练习。
在进行本次实验之前需要先进行下面的操作,在接下来的实验中将会在此基础上进行。
打开 Xfce 终端,依次输入下面语句,进入到 Python 环境,并且导入 webdriver,实例化 Chrome 浏览器后访问蓝桥首页(https://www.lanqiao.cn/ )。
python3 from selenium import webdriver driver = webdriver.Chrome() driver.get("https://www.lanqiao.cn/")
单击元素
模拟鼠标左键操作,多用于按钮、a 链接等元素的单击事件。
语法: .click()。
操作:点击页面上的 【登录】。
Xfce 终端中输入 driver.find_element_by_xpath("//span[contains(text(),'登录')]").click()。 然后输入 driver.switch_to.frame('layui-layer-iframe1'),由于登录弹窗嵌套在 iframe 结构中,所以需要切换进入才能操作,在实验六中会详细介绍。
结果:页面弹出登录窗口。
提示:登录按钮所在的导航栏会根据浏览器的宽度、高度而做出变动,如果语句 driver.find_element_by_xpath("//span[contains(text(),'登录')]").click() 不生效的话,可使用 (driver.find_elements_by_xpath("//span[contains(text(),'登录')]"))[1].click() 。
输入内容
模拟按键输入,常用于输入框输入文本内容。
语法: .send_keys(text)。
操作:在上一个单击元素操作(打开登录弹窗)后,在第一个输入框中输入 “tynam”。
Xfce 终端中输入driver.find_element_by_css_selector("input[placeholder='手机号/邮箱']").send_keys('tynam')。
结果:登录窗口的第一个输入框中输入了文本 “tynam”。
清空内容
用于清除对象中的内容,常用于清除输入框的默认值。
语法: .clear()。
操作:在上一个输入内容操作(第一个输入框中输入文本 “tynam”)后,将第一个输入框中的内容清空。
Xfce 终端中输入 driver.find_element_by_css_selector("input[placeholder='手机号/邮箱']").clear()。
结果:登录窗口的第一个输入框中内容被清空。
获取属性值
元素对象的属性通常指 href、name、value、type 值等。
语法: .get_attribute(attribute)。
操作:在单击元素操作(打开登录弹窗)后,打印【登录】按钮的 class 值。
Xfce 终端中输入 print(driver.find_element_by_css_selector(".login-form-button").get_attribute('class'))。
打印结果:login-form-button ant-btn ant-btn-primary。
获取文本内容
元素对象的文本内容,一般是指元素在页面显示的文本内容。
语法: .text。
操作:在单击元素操作(打开登录弹窗)后,打印【登录】按钮的文本内容。
Xfce 终端中输入 print(driver.find_element_by_css_selector(".login-form-button").text)。
打印结果:登 录。
对象显示状态判断
语法: .is_displayed()。
元素对象在页面中显示,也可能被隐藏。is_displayed() 函数是一个布尔类型的函数,显示则返回 True,反之返回 False。
操作:在单击元素操作(打开登录弹窗)后,判断【登录】按钮的显示状态。
Xfce 终端中输入 driver.find_element_by_css_selector(".login-form-button").is_displayed()。
返回结果: True。
注意:对象显示与对象存在一定要做区分。对象显示指的是元素对象在 HTML 中存在,只是在页面上是否会显示。对象存在指 HTML 中是否存在,如果存在可能会显示在页面上,也可能不会显示在页面。
对象编辑状态判断
语法:.is_enabled()。
多用于判断 input、select 等标签元素的编辑状态。布尔类型的函数,可编辑则返回 True,反之返回 False。
操作:在单击元素操作(打开登录弹窗)后,判断第一个输入框的编辑状态。
Xfce 终端中输入 driver.find_element_by_css_selector("input[placeholder='手机号/邮箱']").is_enabled()。
返回结果: True。
对象选择状态判断
语法: .is_selected()。
用于判断元素的选中状态。布尔类型的函数,选中则返回 True,反之返回 False。
操作:在单击元素操作(打开登录弹窗)后,判断记住密码的复选框选中状态。
Xfce 终端中输入 driver.find_element_by_css_selector('.ant-checkbox-input').is_selected()。
返回结果: False。
如果手动将记住密码的复选框进行勾选,再次执行判断,则返回结果为 True。
通过本次实验对页面中元素对象进行鼠标左键单击、内容输入、获取元素属性值、元素状态判断进行实战,对元素对象的一些操作方法有一个基本掌握。键盘和鼠标事件
本次实验主要介绍通过 Selenium 中提供的一些方法模拟鼠标和键盘的一些行为。键盘输入主要介绍 Keys 和 keyUp/keyDown,鼠标操作主要介绍鼠标右击、双击、悬停事件。
知识点
- Keys
- keyUp/keyDown
- 鼠标右击
- 鼠标双击
- 鼠标悬停
- 鼠标其他事件
其他环境准备
Chrome 浏览器对应版本 chromedriver 已经存放在 /usr/bin/ 目录下,无需再次进行操作。
键盘事件
键盘操作是指模拟用户使用键盘进行操作。有两种方法,一种是通过 send_keys 直接发送键值进行操作,另一种是使 keyUp/keyDow 方法发送键值进行操作。
在进行本次实验之前需要先进行下面的操作,在接下来的实验中将会在此基础上进行。
打开 Xfce 终端,依次输入下面语句,进入到 Python 环境,并且导入 webdriver,实例化 Chrome 浏览器后访问蓝桥首页(https://www.lanqiao.cn/ ),操作打开登录窗口,并切换到登录窗口所在的 iframe 中。
python3 from selenium import webdriver driver = webdriver.Chrome() driver.get("https://www.lanqiao.cn/") driver.find_element_by_xpath("//span[contains(text(),'登录')]").click() driver.switch_to.frame('layui-layer-iframe1')提示:登录按钮所在的导航栏会根据浏览器的宽度、高度而做出变动,如果语句 driver.find_element_by_xpath("//span[contains(text(),'登录')]").click() 不生效的话,可使用 (driver.find_elements_by_xpath("//span[contains(text(),'登录')]"))[1].click() 。
Keys
通过 send_keys 直接发送键值进行操作。实现键盘模拟操作时需要导入 Keys:from selenium.webdriver.common.keys import Keys。
常用的键盘操作及模拟方法如下表:
描述 | 模拟操作方法 |
---|---|
全选(Ctrl+A) | send_keys(Keys.CONTROL, 'a') |
复制(Ctrl+C) | send_keys(Keys.CONTROL, 'c') |
剪切(Ctrl+X) | send_keys(Keys.CONTROL, 'x') |
粘贴(Ctrl+V) | send_keys(Keys.CONTROL, 'v') |
返回键(Esc) | send_keys(Keys.ESCAPE) |
制表键(Tab) | send_keys(Keys.TAB) |
空格键(Space) | send_keys(Keys.SPACE) |
退格键(BackSpace) | send_keys(Keys.BACK_SPACE) |
刷新键(F5) | Send_keys(Keys.F5) |
删除键(Delete) | Send_keys(Keys.DELETE) |
数字键 2(2) | send_keys(Keys.NUMPAD2) |
操作:点击【短信登录】进入到通过验证码登录画面,在【手机号】输入框中输入值 “321”,然后通过 Ctrl + C 和 Ctrl + V 组合键复制到【验证码】输入框中。
-
导入 Keys:from selenium.webdriver.common.keys import Keys。
-
点击【短信登录】进入到通过验证码登录画面 driver.find_element_by_xpath("//div[text()='短信登录']").click()。
-
在【手机号】输入框中输入值 “321” (driver.find_elements_by_css_selector("input[placeholder='手机号']"))[1].send_keys(321)。
-
通过 Ctrl + A 组合键全选【手机号】输入框中的值 (driver.find_elements_by_css_selector("input[placeholder='手机号']"))[1].send_keys(Keys.CONTROL, 'a')。
-
通过 Ctrl + C 组合键复制【手机号】输入框中的值 (driver.find_elements_by_css_selector("input[placeholder='手机号']"))[1].send_keys(Keys.CONTROL, 'c')。
-
通过 .clear() 方法清空【验证码】输入框 driver.find_element_by_css_selector("input[placeholder='验证码']").clear()。
-
通过 Ctrl + v 组合键将内容粘贴到【验证码】输入框 driver.find_element_by_css_selector("input[placeholder='验证码']").send_keys(Keys.CONTROL, 'v')。
结果:【验证码】输入框中输入了值 “321”。
keyUp/keyDown
在 ActionChains 中也提供了 keyUp(theKey)、keyDown(theKey) 和 sendKeys(keysToSend) 方法用来模拟键盘输入。
键盘操作是指模拟用户使用键盘进行操作。通常是操作 Shift、Ctrl 和 Alt 键。常用的有三种方法:
- keyUp(theKey):松开 theKey 键。
- keyDown(theKey):按下 theKey 键。
- sendKeys(keysToSend):发送某个键到当前焦点。
使用时需要导入 ActionChains from selenium.webdriver.common.action_chains import ActionChains。
操作:将鼠标光标放在【手机号】输入框中,使用 ActionChains 提供的方法将【手机号】输入框的值进行全选。
-
导入 ActionChains from selenium.webdriver.common.action_chains import ActionChains。
-
通过 ActionChains 提供的方法全选【手机号】输入框的值 ActionChains(driver).key_down(Keys.CONTROL).send_keys('a').perform()。
结果:【手机号】输入框的值被全部选中。
在上面操作中,在按下了 control 和 a 键后最后还使用了 perform() 方法。perform() 方法就是执行所有 ActionChains 中存储的行为,简单来说就是将整个操作事件进行提交执行。
鼠标事件
鼠标右击
模拟鼠标右键单击事件。
方法: .context_click(element)。
操作:在当前页面进行鼠标右击。
-
定位需要右击的元素对象 element = (driver.find_elements_by_css_selector('.login-form-button'))[2]。
-
进行鼠标右击操作 ActionChains(driver).context_click(element).perform()
结果:鼠标右击后打开了,右键菜单。
鼠标双击
模拟鼠标鼠标左键双击事件。
方法:.double_click(element)。
操作:关闭登录弹框页面,点击蓝桥首页轮播图的右滑箭头。
-
在关闭登录框页面时需要退出 iframe 框体,回到之前的操作页面 driver.switch_to_default_content()。
-
关闭登录弹框页面 driver.find_element_by_class_name('layui-layer-close').click()。
结果:可以看的轮播图的图片向左移动了两次。
鼠标悬停
将鼠标悬停在某个具体元素上。
方法: .move_to_element(element)。
操作:进入到【社区】页面,将鼠标移动到页面导航菜单的【实验楼】元素上。
- 进入到【社区】页面 driver.get('https://www.lanqiao.cn/questions/')。
- 定位【课程】元素 element = driver.find_element_by_xpath("//button[contains(text(),'课程')]")。
- 进行鼠标悬停操作 ActionChains(driver).move_to_element(element).perform()。
结果:可以看的鼠标悬停在【课程】元素上调出了二级菜单。
鼠标其他事件
除过以上介绍的几种鼠标事件,还有其他的一些鼠标操作方法。
事件 | 方法 | 使用 | 使用说明 |
---|---|---|---|
鼠标拖动 | drag_and_drop(source, target) | ActionChains(driver).drag_and_drop(source, target).perform() | 将 source 对象拖放到 target 对象的位置 |
单击鼠标左键不放 | click_and_hold(element) | ActionChains(driver).click_and_hold(element).perform() | 在元素 element 进行鼠标左击并且不放松 |
移动到元素具***置 | move_to_element_with_offset(element, xoffset, yoffset) | ActionChains(driver). move_to_element_with_offset(element, 20, 10) .perform() | 将鼠标移动到 element 中 x=20,y=10 的位置,以元素 element 的左上处为原点 x=0,y=0。向右为 x 轴的正坐标,向下为 y 轴的正坐标。 |
释放鼠标 | release(element) | ActionChains(driver).release(element) |
其他常用操作
本次实验主要介绍 selenium 提供的对下拉框、特殊 Dom 结构操作、frame 与 iframe 结构、JavaScript、截屏的操作方法。
知识点
- 下拉框操作
- 特殊 Dom 结构操作
- frame 与 iframe 结构
- JavaScript 调用
- 屏幕截图
其他环境准备
Chrome 浏览器对应版本 chromedriver 已经存放在 /usr/bin/ 目录下,无需再次进行操作。
下拉框操作
from selenium.webdriver.support.ui import Select from selenium.webdriver.support.select import Select
Select 中对下拉框的选择项可以进行获取、选择、取消选择操作。
获取下拉框的选择项:
- options: 返回所有的选择项。
- all_selected_options: 返回所有已选中的选择项。
- first_selected_options: 返回选中的第一个选择项。
对选择项进行选择:
- select_by_index(index): 通过索引选择。
- select_by_value(value): 通过 value 值选择。
- select_by_visible_text(text): 通过文本值选择。
取消选择项的选择:
- deselect_all(): 取消全部的已选项。
- deselect_by_index(index): 根据索引取消选择项。
- deselect_by_value(value): 根据 value 值取消选择项。
- deselect_by_visible_text(text): 根据文本值取消选择项。
操作:对下拉框练习页面中下拉框进行操作。
- Xfce 终端中依次输入下面语句,启动浏览器并且访问下拉框练习页面。
python3 from selenium import webdriver from selenium.webdriver.support.select import Select driver = webdriver.Chrome() driver.get("https://labfile.oss.aliyuncs.com/courses/2335/60-0.html")
3.获取下拉框的第二个选项项文本值。索引是从 0 开始的,所以第二个选择项的索引为 1。print((Select(select).options)[1].text)。
结果:Html
4.通过索引选择第二个选择项 Select(select).select_by_index(1)。结果:下拉框显示的值是第二个选择项 “Html”。
5.通过文本值选择 “PHP” 选择项 Select(select).select_by_visible_text("PHP")。结果:下拉框显示的值是 “PHP”。
注:取消选择,适合下拉框是多选的情况。即存在属性 multiple=“multiple”。
特殊 Dom 结构操作
特殊 Dom 结构是指 Selenium 不能直接对元素进行操作,需要进行定位切换到它所在的 Dom 结构后才能对其元素进行操作。
操作:
Xfce 终端中依次输入下面语句,启动浏览器并且访问弹窗练习画面 https://labfile.oss.aliyuncs.com/courses/2335/60-1.html 。
python3 from selenium import webdriver driver = webdriver.Chrome() driver.get("https://labfile.oss.aliyuncs.com/courses/2335/60-1.html")
Windows 弹窗
常见的 Windows 弹窗有 alert、confirm 和 prompt 三种形式,由于这些弹窗结构不属于页面层结构,而是浏览器层结构,因此想要操作这些弹窗需要使用 driver.switch_to.alert 方法,切换到 Windows 弹窗后才可以进行操作。
alert 类提供了如下一些操作方法:
- accept():确定。
- dismiss():取消。
- text:获取弹出框里面的内容。
- send_keys(keysToSend):发送文本,对有提交需求的 prompt 提示消息框。
操作:点击弹窗练习画面中的【windows 弹窗】,打印弹出框里面的文本内容,然后点击【确定】。
- 点击【windows 弹窗】按钮 driver.find_element_by_css_selector("#windows input").click()。
结果:画面弹出了一个窗口。
结果:弹窗关闭。
非 Windows 弹窗
非 Windows 弹窗通常是通过单击事件改变 Dom 元素隐藏或显示的属性来控制窗口的显示。
操作:点击弹窗练习画面中的【非 windows 弹窗】,然后关闭弹窗。
- 点击【非 windows 弹窗】 driver.find_element_by_css_selector("#noWindows input").click()。
结果:打开弹窗。
结果:弹窗关闭。
frame 与 iframe 结构
frame 标签有 frameset、frame 和 iframe 三种,frameset 和普通标签是一样的,不需要特殊处理,正常定位即可。但是如果是 iframe 和 frame 则需要特殊的处理。WebDriver 在 HTML 中查找元素时不会自动在 frame 结构中查找,需要引导 WebDriver 进入到 frame 结构中。使用 Selenium 提供的 switch_to.frame(reference) 方法可以进入到 frame 结构。
操作:访问 iframe 练习画面,并且在输入框中输入内容 “tynam”。
- Xfce 终端中依次输入下面语句,启动浏览器并且访问 iframe 练习画面 https://labfile.oss.aliyuncs.com/courses/2335/60-2.html 。
python3 from selenium import webdriver driver = webdriver.Chrome() driver.get("https://labfile.oss.aliyuncs.com/courses/2335/60-2.html")
画面如图所示,由于输入框嵌套在 iframe 结构中,要想操作输入框则必须先切换到 iframe 中。
结果:输入框中输入了内容 “tynam”。
switch_to 除了切进 frame 结构,还有其他一些用法:
- switch_to_default_content():切出 frame,切换到 frame 结构后 WebDrvier 的操作都会在 frame 中进行,如果要对 frame 外的元素进行操作,则需要切出 frame 结构。
- switch_to.parent_frame():切换到上一层的 frame,多用于层层嵌套的 frame 结构。
JavaScript 调用
在自动化测试中,对某些元素的操作不能直接使用 Selenium 提供的方法,需要借助 javaScript 语句才能操作。在 WebDriver 中可通过 execute_script() 方法来执行 JavaScript 脚本。
例如在蓝桥首页点击【1+X 证书】后会重新打开一个窗口,而我们不需要重新打开,需要在当前窗口打开。控制是在当前窗口还是重新开启一个窗口是由元素属性 target="_blank" 来控制的。我们只需要使用 JavaScript 操作元素去掉 target 属性即可达到目的。
操作:
- Xfce 终端中依次输入下面语句,启动浏览器并且访问蓝桥首页。
python3 from selenium import webdriver driver = webdriver.Chrome() driver.get("https://www.lanqiao.cn/")2.JS 中获取【1+X 证书】元素,然后进行移除操作。最后执行 JS 语句。
js = "var el = document.getElementsByClassName('minor-nav-link')[3];" \ "el.removeAttribute('target','_blank');" driver.execute_script(js)
结果:在当前窗口打开 1+X 证书页面。
屏幕截图
当我们在自动化测试过程中,如果程序运行失败,那么就进行自动截取当前页面,保留记录,方便查看运行失败的原因。
WebDriver 提供了 4 种截屏方法:
- save_screenshot(): 获取当前窗口的屏幕截图,并且以 png 文件格式存储。
- get_screenshot_as_base64(): 以 base64 编码字符串的形式获取当前窗口的屏幕截图。
- get_screenshot_as_file(): 获取当前屏幕截图,使用完整的路径。如果有任何 IO error,返回 False,否则返回 True。
- get_screenshot_as_png(): 以二进制数据形式获取当前窗口的屏幕截图。
操作:截取当前窗口,并且将图片命名为 screenshot.png 并且保存在 /home/shiyanlou/Code/ 路径下。
driver.save_screenshot('/home/shiyanlou/Code/screenshot.png')本次实验主要是在自动化测试过程中对一些特殊行为的操作进行实战练习,在实际项目中可能还会遇到许多其他特殊结构的操作,希望通过本次实验可以举一反三,可以从容应对各种操作。
时间等待和浏览器配置项
在脚本开发中,可能会因为页面未完全加载完成或由于浏览器对象的一些默认属性导致自动化测试出现停滞,本次实验将介绍脚本开发中三种时间时间等待和实例化浏览器对象时,通过一些设置对浏览器的某些默认属性进行更改。
知识点
- 时间等待
- 限制页面加载时间
- 配置 Chrome 浏览器
- SSL 证书错误处理
- 获取环境信息
其他环境准备
Chrome 浏览器对应版本 chromedriver 已经存放在 /usr/bin/ 目录下,无需再次进行操作。
时间等待
在网页操作中,可能会因为带宽、浏览器渲染速度、机器性能等原因造成页面加载缓慢,在自动化测试过程中造成某些元素还没有完全加载完成就进行下一步操作,导致程序抛出异常 “未找到定位元素”。此时可以通过添加等待时间来解决。
在 Python + Selenium 中,有三种时间等待的方式,强制等待、显示等待和隐式等待。
强制等待
强制等待是设置一个固定的线程休眠时间,通过 sleep(time) 方法来实验。它是 Python time 模块提供的一种非智能等待,如果设置等待时间为 3 秒则程序执行过程中就会等待 3 秒时间,多用于程序执行过程中观察执行效果。
time.sleep(time) 的时间单位默认是秒。
操作:验证 sleep(time) 是固定等待时间。
-
在 /home/shiyanlou/Code/ 目录下新建 time_practice.py 文件。
-
在 time_practice.py 中编辑代码,设置等待时间为 3 秒,且打印等待时间前后的时间。
# -*-coding: utf-8-*- import time from datetime import datetime print(datetime.now()) # 等待 3s time.sleep(3) print(datetime.now())3.然后打开 Xfce 终端,输入下面命令运行 time_practice.py 脚本。
python3 /home/shiyanlou/Code/time_practice.py
结果:命令执行后打印等待时间前后的时间差为 3s。
显示等待
显示等待是指针对某个元素设置一个等待时间,通过 WebDriverWait() 方法实现。WebDriverWait() 一般会和 until() 和 until_not() 方法配合使用。例如,WebDriverWait().until() 程序执行时会对 until 中的返回结果进行判断,从而决定是否进行下一步。如果返回结果为 True 则进行下一步操作;如果返回结果为 False 则会不断地去判断 until 中的返回结果,直至超过设置的等待时间,然后抛出异常。
WebDriverWait().until_not() 与 WebDriverWait().until() 的判定结果相反。 WebDriverWait().until_not() 执行时如果 until_not 中返回结果为 False 则执行下一步,反之则不断地去判断 until_not 中的返回结果。
请看 WebDriverWait 的定义:
class WebDriverWait(object): def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
共有 4 个参数可供使用:
- driver:webdriver 实例,Chrome、IE、Firefox 等。
- timeout:超时时间,即等待的最长时间。
- poll_frequency:调用后面操作的频率,默认为 0.5s。
- ignored_exceptions:忽略的异常,默认为 NoSuchElementException。
在使用 WebDriverWait 是需要导入 from selenium.webdriver.support.ui import WebDriverWait。
until 中的判断通常通过 expected_conditions 类中的方法进行。使用时需要导入 from selenium.webdriver.support import expected_conditions。
操作:设置显示等待 10s,每隔 1s 尝试一次。
打开 Xfce 终端,输入下面语句。
python3 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 driver = webdriver.Chrome() driver.get('https://labfile.oss.aliyuncs.com/courses/2335/20.html') WebDriverWait(driver, 10, 1).until(expected_conditions.visibility_of_element_located((By.ID, 'search')))
结果:在查找 id="search" 元素时,每隔 1s 尝试查找一次元素显示在页面中,如果出现则进行下一步操作,直到时间超过 10s 程序抛出异常。
使用 expected_conditions 类常见的页面元素的判断方法:
- title_is: 判断当前页面的 title 是否等于预期结果。
- title_contains: 判断当前页面的 title 是否包含预期的字符串。
- presence_of_element_located: 判断元素是否被加到 dom 树下,该元素不一定可见。
- visibility_of_element_located: 判断元素是否可见,并且元素的宽和高都不为 0。
- presence_of_all_elements_located: 判断至少有一个元素存在于 dom 树下。
- text_to_be_present_in_element: 判断元素中的 text 文本是否包含预期的字符串。
- text_to_be_present_in_element_value: 判断元素中的 value 属性值是否包含预期的字符串。
- frame_to_be_availabe_and_switch_to_it: 判断 frame 是否可以 switch 进去,如果可以,则返回 True,并且 switch 进去,反之则返回 False。
- invisibility_of_element_located: 判断元素是否不存在于 dom 树或不可见。
- element_to_be_clickable: 判断元素可见并且可以操作。
- element_to_be_selected: 判断元素是否被选中。
- element_selection_state_to_be: 判断元素的选中状态是否符合预期。
- alert_is_present: 判断页面上是否存在 alert。
隐式等待
隐式等待是全局的针对所有元素设置的等待时间,通过 implicitly_wait(time) 方法来实现。这是 WebDriver 提供的一种智能等待,也称是对 driver 的一种隐式等待,使用时只需在代码块中设置一次。
操作:设置隐式等待时间为 10s。
打开 Xfce 终端,输入下面语句。
python3 from selenium import webdriver driver = webdriver.Chrome() driver.implicitly_wait(10)结果:设置的 driver 隐式等待时间为 10 秒,则 driver 在以后操作中执行 find_element_by_xxx() 时如果找到元素就会立即执行下一个动作,不会完全等够 10 秒后才执行下一个动作。如果超过 10 秒还没有找到该元素,则抛出未能定位到该元素的错误。
限制页面加载时间
限制页面加载时间即设置页面加载的超时时间。通过 set_page_load_timeout(time) 方法实现。
操作:访问蓝桥首页,并且设置页面加载的超时时间为 10s。
打开 Xfce 终端,输入下面语句。
python3 from selenium import webdriver from selenium.common.exceptions import TimeoutException driver = webdriver.Chrome() driver.set_page_load_timeout(10) try: driver.get('https://www.lanqiao.cn/') except TimeoutException: print('页面加载超过10秒,强制停止加载....') driver.execute_script('window.stop()')结果:在访问蓝桥首页中,当页面加载时间超过 10s 后浏览器停止加载。
配置 Chrome 浏览器
屏蔽浏览器对 Selenium 的检测
在使用 Selenium 启动浏览器后,浏览器会检测到是被自动测试软件控制,而非人的行为,会提示 “Chrome 正受到自动测试软件的控制”。
解决方法:屏蔽浏览器的检测。在实例化浏览器对象进行设置:
options = webdriver.ChromeOptions() options.add_experimental_option('excludeSwitches', ['enable-automation']) driver = webdriver.Chrome(options=options)
ChromeOptions 是一个配置 Chrome 启动时属性的类,使用该类可以对 Chrome 进行一些设置:
- 设置 Chrome 二进制文件的位置: binary_location。
- 添加启动参数: add_argument。
- 添加拓展应用: add_extension, add_encoded_extension。
- 添加实验性质的设置参数: add_experimental_option。
- 设置调试器地址: debugger_address。
禁止图片和视频加载
有时候为了提高网速,页面中不太关心图片和视频,此时可以禁止图片和视频加载:
options = webdriver.ChromeOptions() prefs = {"profile.managed_default_content_settings.images":2} options.add_experimental_option('prefs', prefs) driver = webdriver.Chrome(chrome_options = options)
设置编码
在项目测试中浏览器中默认的编码格式可能不是我们想要的,此时可以设置自己的默认编码格式,例如设置编码格式为 UTF-8:options = webdriver.ChromeOptions() options.add_argument('lang=zh_CN.UTF-8') driver = webdriver.Chrome(chrome_options = options)
其他参数
除了以上几种参数外,还有以下参数也可以设置:
- 添加代理:options.add_argument("--proxy-server=http://192.10.1.1:8888")。
- 模拟移动设备:options.add_argument('user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"')。
- 禁用 JS:option.add_argument("--disable-javascript")。
- 禁用插件:option.add_argument("--disable-plugins")。
- 禁用 java:option.add_argument("--disable-java")。
- 启动时最大化:option.add_argument("--start- maximized")。
- 添加扩展插件:options.add_extension('C:/extension/xxxx.crx')。
SSL 证书错误处理
SSL(Secure Sockets Layer,安全套接层)证书错误,它是浏览器一种安全机制引起的问题。一般情况下在以 HTTPS 模式访问目标 URL 时浏览器会提示安全问题或非信任站点。针对不同的浏览器有不同的处理方法,但是都是在实例化浏览器对象时添加设置参数,忽略不信任的证书或信任证书。下面是 Chrome 浏览器提示 SSL 证书错误。
Chrome 浏览器处理方式
Chrome 浏览器解决办法是在 ChromeOptions() 中添加 “--ignore-certificate-errors” 为 True 的选项:
from selenium import webdriver options = webdriver.ChromeOptions() # 添加忽视证书错误选项 options.add_argument('--ignore-certificate-errors') driver = webdriver.Chrome(chrome_options=options) driver.get('URL')
Firefox 浏览器处理方式
Firefox 浏览器解决办法是 FirefoxProfile() 中添加 “accept_untrusted_certs” 为 True 的选项:
from selenium import webdriver profile = webdriver.FirefoxProfile() # 添加接受不信任证书选项 profile.accept_untrusted_certs = True driver = webdriver.Firefox(firefox_profile=profile) driver.get('URL')
IE 浏览器处理方式
注:IE 浏览器不能在实验环境中安装,此项操作可在 Windows 系统中实战练习。
IE 浏览器可以通过使用 JavaScript 语句在页面中操作,忽略不信任证书的提示继续访问。
如上图所示【转到此网页(不推荐)】的 id="overridelink",可通过执行 JavaScript 语句 document.getElementById('overridelink').click() 执行页面的单击操作。
from selenium import webdriver driver = webdriver.Ie() driver.get('URL') # 选择继续访问 URL js = "javascript:document.getElementById('overridelink').click();" driver.get(js) driver.execute_script(js)
获取环境信息
在项目自动化测试中有时候需要对运行的环境有更详细的了解,比如程序在什么浏览器中执行、浏览器的版本号是多少等,此时可通过 capabilities[] 获取。
操作:获取系统名称后浏览器名称。
打开 Xfce 终端,依次输入下面语句。
python3 from selenium import webdriver driver = webdriver.Chrome() print(driver.capabilities['browserName'])
打印结果:chrome
当然,还可以获取运行环境的其他信息:
- browserName:获取浏览器名称。
- browserVersion:获取浏览器版本。
- platformName:获取操作系统名称。
- proxy:获取代理信息。
- timeouts:获取超时时间。返回的是一个字典。
UnitTest 测试框架一
UnitTest 是 Python 自带的单元测试框架,在自动化测试框架中被用来组织测试用例的执行、断言和日志记录。除此之外,UnitTest 还提供了丰富的断言方法,用于判断测试用例执行后的结果与预期结果是否一致。
知识点
- UnitTest 简介
- VS Code 的使用
- TestFixture
- TestCase
- 断言 Assert
其他环境准备
Chrome 浏览器对应版本 chromedriver 已经存放在 /usr/bin/ 目录下,无需再次进行操作。
UnitTest 简介
UnitTest 单元测试框架是受到 JUnit 的启发,与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化,配置共享和关机代码测试。支持将测试样例聚合到测试集中,并将测试与报告框架独立。
UnitTest 官方文档参考网站:https://docs.python.org/zh-cn/3.8/library/unittest.html
UnitTest 主要有四部分内容:
- TestFixture:测试脚手架,测试所需要进行的准备工作,以及所有相关的清理操作。
- TestCase:测试用例,一个 TestCase 的实例就是一个测试用例。它检查输入特定的数据时的响应。
- TestSuite:测试套件,用于归档需要一起执行的测试。将多个测试用例集合在一起就是一个 TestSuite。
- TestRunner:测试运行器,用于执行和输出测试结果的组件。
在获得测试用例后,将测试用例进行归档,集合在一起形成一个 TestSuite,然后 TextTestRunner 执行 run() 方法,将以 test 开头的方法当作测试用例就行执行,最后使用 TextTestResult 生成测试报告。完成一个完整的自动化测试流程。
VS Code 的使用
在此之前已经进行了七次实验,直到现在才使用 Python IDE,这是因为在前面的实验中希望多通过手动一个一个词进行练习,避免过多依赖。
在脚本开发中有多款 IDE 可供选择,比如 Pycharm、VS Code 等。在此选择 VS Code,有诸多优点:
- 轻量级。
- 界面美观,代码高亮效果好。
- 功能强大且易用。
- 跨平台 支持多种平台 Windows、Mac、Linux 等等。
VS Code 环境准备
在使用 VS Code 进行自动化脚本开发时,需要做一些准备工作。
-
在桌面启动 VS Code 程序。
-
安装 Python 插件。
如果不安装 Python 插件,则点击 【调试】 -> 【启动调试(快捷键 F5)】是不能运行代码的。会提示选择环境,而根本就没有可选环境。
在 VS Code 的应用商店(快捷键:Ctrl + Shift + X)里搜索 Python 插件,并安装。
使用快捷键 Ctrl + Shift + P(或 F1),在打开的输入框中输入 Python: Select Interpreter 搜索,选择 Python3.5 解析器。
直接在终端输入相应命令就可以进行查看、安装外部库。终端就相当于命令提示符(cmd)。
查看已安装包列表 pip3 list 。
安装外部库 pip3 install xxx 。例如安装 Selenium 库则可写成 pip3 install Selenium 。
TestFixture
TestFixture,测试脚手架。在自动化测试中许多用例都需要执行相同的测试准备和测试清理工作。这种情况下就可以使用 TestFixture 将准备工作、清理工作抽象出来实现代码复用。在 UnitTest 中,提供了两种方法用于准备工作 setUp() 和 setUpClass() ,了两种方法用于清理工作 tearDown() 和 tearDownClass()。
- setUp():测试用例执行前的准备工作,每个测试用例的前置条件。
- tearDown():测试用例执行完成后的善后工作,每个测试用例的后置条件。
- setUpClass():结合 @classmethod 装饰器一起使用,是所有测试用例执行的前置条件。
- tearDownClass():结合 @classmethod 装饰器一起使用,是所有测试用例执行的后置条件。
操作:分别使用 setUp()、tearDown()、setUpClass() 和 tearDownClass() 进行验证在测试执行过程中的运行状况。
-
使用 VS Code 在 /home/shiyanlou/Code/ 下新建文件 test_fixture.py。
-
在 test_fixture.py 编写如下代码。两条测试用例方法,分别一条前置条件 setUp() 和 setUpClass(),分别一条后置条件 tearDown() 和 tearDownClass()。
# -*-coding: utf-8-*- import unittest class TestFixture(unittest.TestCase): @classmethod def setUpClass(cls): print("所有用例执行的前置条件 setUpClass\n") @classmethod def tearDownClass(cls): print("所有用例执行的后置条件 tearDownClass") def setUp(self): print("测试用例执行的前置条件 setUp") def tearDown(self): print("测试用例执行的后置条件 tearDown") def test_01(self): print("测试用例1") def test_02(self): print("测试用例2") if __name__ == '__main__': unittest.main()3.VS Code 中进行运行。
运行结果:
setUpClass() 和 tearDownClass() 只执行一次并且在所有测试用例的前后,而 setUp() 和 tearDown() 在每一条测试用例的前后执行,有多少条测试用例就会执行多少次。
TestCase
每一个继承 TestCase 类的子类里面实现的具体的方法(以 test 开头的方法)都是一条用例。 一个完整的测试流程,包括测试前准备环境的搭建,执行测试代码,以及测试后环境的还原。元测试的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。
操作:写一个类继承 unittest.TestCase,类中有两个方法,方法命名时一个以 test 开头,一个非 test 开头。
-
使用 VS Code 在 /home/shiyanlou/Code/ 下新建文件 test_case.py。
-
在 test_case.py 编写如下代码。一个类继承 unittest.TestCase,类中有两个方法,方法命名时一个以 test 开头,一个非 test 开头。
# -*-coding: utf-8-*- import unittest class TestCase(unittest.TestCase): def setUp(self): print("测试用例开始执行") def tearDown(self): print("测试用例执行结束") def add(self, a, b): return a+b def test_add(self): print("test开头的方法") result = self.add(2, 3) assert result == 5 def add_test(self): print("非test开头的方法") result = self.add(2, 3) assert result == 5 if __name__ == '__main__': unittest.main()3.VS Code 中进行运行。
测试用例开始执行 test开头的方法 测试用例执行结束 . ---------------------------------------------------------------------- Ran 1 test in 0.009s OK只运行了以 test 开头命名的方法。在 UnitTest 框架中只将 test 开头的方法当作测试用例执行。
断言 Assert
断言用于判断实际结果和预期结果是否相等。如果断言失败则会标记该条测试用例执行失败。在 Python + UnitTest 结构中,有两种断言可供使用,一种是 Python 语言中提供的断言 Assert,另一种是 UnitTest 中提供的断言方法。
断言主要有 3 种类型:
- 布尔类型断言,即真或假,如果判断为真,则返回 True,反之返回 False。
- 比较类型断言,例如比较两个变量值的大小,如果判断为真,则返回 True,反之返回 False。
- 复杂类型断言,例如判断两个列表是否相等,如果相等,则返回 True,反之返回 False。
UnitTest 中提供的常用断言方法如下:
方法 | 说明 |
---|---|
assertEqual(a, b) | 判断 a 等于 b |
assertNotEqual(a, b) | 判断 a 不等于 b |
assertIs(a, b) | 判断 a 是 b |
assertIsNone(a) | 判断 a 是 None |
assertIn(a, b) | 判断 a 在 b 中,包含相等 |
assertGreater(a, b) | 判断 a 大于 b |
assertGreaterEqual(a, b) | 判断 a 大于等于 b |
assertLess(a, b) | 判断 a 小于 b |
assertLessEqual(a, b) | 判断 a 小于等于 b |
assertListEqual(list1, list2) | 判断列表 list1 等于 list2 |
assertTupleEqual(tuple1, tuple2) | 判断元组 tuple1 等于 tuple2 |
assertDictEqual(dict1, dict2) | 判断字典 dict1 等于 dict2 |
操作:分别用 Python 和 UnitTest 提供的断言各写一条成功和失败的用例。
-
使用 VS Code 在 /home/shiyanlou/Code/ 下新建文件 test_assert.py。
-
在 test_assert.py 编写如下代码。分别使用 Python 和 UnitTest 提供的断言各写一条成功和失败的用例。
# -*-coding: utf-8-*- import unittest, time class TestAssert(unittest.TestCase): def setUp(self): time.sleep(1) def test_python_success(self): print("python提供的断言,断言成功") assert 1 == 1 def test_python_fail(self): print("python提供的断言,断言失败") assert 1 > 2 def test_unittest_success(self): print("unittest提供的断言,断言成功") self.assertIsNone(None) def test_unittest_fail(self): print("unittest提供的断言,断言失败") self.assertIn('hello1', 'hello') if __name__ == '__main__': unittest.main()3.VS Code 中进行运行。
python提供的断言,断言失败 Fpython提供的断言,断言成功 .unittest提供的断言,断言失败 Funittest提供的断言,断言成功 . ====================================================================== FAIL: test_python_fail (__main__.TestAssert) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/shiyanlou/Code/test_assert.py", line 17, in test_python_fail assert 1 > 2 AssertionError ====================================================================== FAIL: test_unittest_fail (__main__.TestAssert) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/shiyanlou/Code/test_assert.py", line 25, in test_unittest_fail self.assertIn('hello1', 'hello') AssertionError: 'hello1' not found in 'hello' ---------------------------------------------------------------------- Ran 4 tests in 4.003s FAILED (failures=2)
断言的成功与否不会影响代码的执行,即使断言失败代码也会继续执行。自动化测试中使用断言可判断测试用例是否通过。如结果所示使用 UnitTest 提供的断言比使用 Python 提供的断言在测试失败的结果中更详细的给出了失败原因,所以在自动化测试中要尽可能地使用 UnitTest 提供的断言,以方便查找断言失败的原因。
在上面的测试结果中可以看到,出现了 “ F ”、“ . ” 的标识,这些标识是对测试结果的一种表示。常见的测试标识有:
- .: 点,表示测试通过。
- F:failure,表示测试失败,未通过。
- s:skip,表示测试跳过,不执行该条测试用例。
- x:预期结果为失败的测试用例。