1.技术浅谈
1.1 何为 selenium
Selenium广泛使用的开源Web UI(用户界面)自动化测试套件之一,支持跨不同浏览器,平台和编程语言的自动化
Selenium通过使用各种语言的驱动程序支持各种编程语言。Selenium支持的语言包括C#,Java,Perl,PHP,Python和Ruby。目前,Selenium + python 简直就是黄金搭档。
1.2 功能特性
开源的Web测试框架
Selenium IDE为创作测试提供了回放和录制功能,而无需学习测试脚本语言
Selenium支持各种操作系统,浏览器和编程语言
支持并行测试执行,从而减少了时间并提高了测试效率
与其他自动化测试工具相比,Selenium需要的资源更少。
WebDriver API已经尝试集于Selenium中,这是对Selenium进行的最重要的修改之一。
Selenium Web驱动程序不需要服务器安装,测试脚本直接与浏览器交互
Selenium命令根据不同的类进行分类,使其更易于理解和实现
1.3 局限性
Selenium不支持桌面应用程序的自动化测试
无法使用Selenium对Web服务(如SOAP或REST)执行自动化测试。
使用者应该至少知道或熟悉一种受支持的编程语言,以便在Selenium WebDriver中创建测试脚本。
Selenium没有任何内置的报告功能; 必须依赖JUnit和TestNG等插件来获取测试报告。
无法对图像执行测试。需要将Selenium与Sikuli集成以进行基于图像的测试。
1.4 工具套件
Selenium不仅仅是一个工具,而是一套软件,每个软件都有不同的方法来支持自动化测试。 它由四个主要组成部分组成,包括:
- Selenium集成开发环境(IDE)
- Selenium RC远程控制器(现已弃用)
- webdriver
- Selenium Grid
Selenium IDE实现为Firefox扩展,在测试脚本上提供记录和回放功能,允许测试人员以多种语言导出录制的脚本,可以在Webdriver中使用这些导出的脚本
SeleniumWebDriver提供了一个编程接口来创建和执行测试用例。 编写测试脚本是为了识别网页上的Web元素,然后对这些元素执行所需的操作。
Selenium Grid允许在不同的机器上并行运行不同浏览器的测试。 简单来说,可以在运行不同浏览器和操作系统的不同机器上同时运行测试。
2.Selenium - webdriver的环境配置
想要Python通过selenium操作浏览器,有两个前提条件:
- 下载selenium模块
- 安装selenium的浏览器驱动 webdriver
selenium模块下载
1 | pip install selenium |
浏览器驱动下载
Chrome的驱动下载地址:
- https://registry.npmmirror.com/binary.html?path=chromedriver/
- http://chromedriver.storage.googleapis.com/index.html
选择和浏览器匹配的版本,压缩包内的chromedriver.exe可执行文件,将该文件移动到Python的安装目录中的Scripts目录(因为该目录已经添加到了系统Path中,你移动到别的目录也行,只要将目录添加到系统的Path中)
Firefox的驱动下载地址:https://github.com/mozilla/geckodriver/releases下载到本地是一个`geckodriver.exe`可执行文件,同样的,你将该文件移动到Python安装目录中的`Scripts`目录
3.Selenium - webdriver基本操作
3.1 常用类和方法
1 | from selenium import webdriver # 驱动浏览器 |
3.2 选择器
形式1
1 | from selenium import webdriver # 驱动浏览器 |
形式2:
1 | from selenium.webdriver.common.by import By # 选择器,以什么方式选择标签元素 |
当获取了标签对象后,可以接着获取这个标签的属性、内容、css样式和子标签:
1 | import time |
注意:按钮提交时,除了click之外,还有一个submit,区别是click就是单纯的点一下。而submit是完成了表单提交,需要携带表单信息进行提交的。如果是button按钮的话,只能使用click,而不能使用submit。
3.3 keys键盘事件
1 | from selenium.webdriver.common.keys import Keys # 键盘相关 |
3.4 鼠标事件
1 | from selenium.webdriver import ActionChains # 鼠标的相关操作,比如滑动验证 |
3.5 浏览器的前进和后退
1 | # 使用selenium自动的模拟前进(forward)后退(back)和刷新(refresh)三个动作 |
3.6 文件操作
上传文件在send_keys中添加上文件路径即可。
1 | driver.find_element_by_name('file_obj').send_keys(r'C:\Users\Desktop\abc.jpg') |
3.7 元素定位
- 见3.2
补充
根据link text 模糊定位
drier.find_element_by_partial_link_text,模糊定位,获取多个,返回一个drier.find_elements_by_partial_link_text,模糊定位,以列表的形式返回。
根据xpath定位
drier.find_element_by_xpath,获取多个,返回一个drier.find_elements_by_xpath,以列表的形式返回
css selector定位,推荐
drier.find_element_by_css_selector,获取多个,返回一个drier.find_elements_by_css_selector,以列表的形式返回。
3.8 XPath定位
XPath中节点匹配的基本方法。
路径匹配
路径匹配与文件路径的表示相仿,比较好理解,有以下几个符号:
- 用
/表示节点路径,如/A/B/C表示节点A的子节点B的子节点C,/表示根节点。 - 用
//表示所有路径以//后指定的子路径结尾的元素,如//D表示所有的D元素;如果是//C/D表示所有父节点为C的D元素。 - 用
*表示路径的通配符,如/A/B/C/*表示A元素下的B元素下的C元素下的所有子元素。
位置匹配
对于每一个元素,它的各个子元素都是有序的:
/A/B/C[1]表示A元素下的B元素下的C元素下的第一个子元素。/A/B/C[last()]表示A元素下的B元素下的C元素下最后一个子元素。/A/B/C[position()>2]表示A元素下的B元素下的C元素下的位置号大于2的元素。
属性及属性值
在XPath中可以利用属性及属性值来匹配元素,需要注意的是:元素的属性名前要有@前缀,例如:
//B[@id]表示所有具有属性id的B元素。//B[@*]表示所有具有属性的B元素。//B[not(@*)]表示所有不具有属性的B元素。//B[@id="b1"]表示id值为b1的B元素。
3.9 Css Selector定位
推荐,因为前端人员一般使用其精准定位
根据tag name定位
1
find_element_by_css_selector("form")
根据id定位
1
2
3
4# 直接根据id定位
find_element_by_css_selector("#kw").send_keys(text)
# 通过标签加id的形式定位
find_element_by_css_selector('input#su').click()根据class定位
1
2
3
4
5
6
7
8# 直接根据clas定位
find_element_by_css_selector(".s_ipt").send_keys(text)
# 标签加class
find_element_by_css_selector('input.s_btn').click()
# 多class属性定位
find_element_by_css_selector('.bg.s_btn').click()
# input加多属性
find_element_by_css_selector('input.bg.s_btn').click()根据元素属性定位
1
2
3
4
5
6
7
8
9
10# 精准匹配
find_element_by_css_selector('input[name=wd]').send_keys(text) # 属性名=属性值
# 多属性
drier.find_element_by_css_selector('input[type="submit"][value="百度一下"]').click()
# 模糊匹配(正则表达式匹配)
find_element_by_css_selector('input[id ^="k"]').send_keys(text) # ^= 匹配以 k 开头的id
find_element_by_css_selector('input[id $="w"]').send_keys(text) # $= 匹配以 w 结尾的id
find_element_by_css_selector('input[value *="度一"]').click() # *= 匹配 value 值的中间部分常用正则比配
E[attr]:只使用属性名,但没有确定任何属性值;
E[attr=”value”]:指定属性名,并指定了该属性的属性值;
E[attr~=”value”]:指定属性名,并且具有属性值,此属性值是一个词列表,并且以空格隔开,其中词列表中包含了一个value词,而且等号前面的“〜”不能不写;
E[attr^=”value”]:指定了属性名,并且有属性值,属性值是以value开头的;
E[attr$=”value”]:指定了属性名,并且有属性值,而且属性值是以value结束的;
E[attr*=”value”]:指定了属性名,并且有属性值,而且属值中包含了value;
E[attr|=”value”]:指定了属性名,并且属性值是value或者以“value-”开头的值(比如说zh-cn);
子标签定位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 匹配子元素, A>B
find_element_by_css_selector('form>span>input').send_keys(text)
# 匹配后代元素, A空格B
find_element_by_css_selector('form span input').send_keys(text)
# 匹配第一个后代元素
find_element_by_css_selector('form :first-child').get_attribute('name') # 返回form表单中隐藏的第一个input标签
find_element_by_css_selector('form span:first-child').get_attribute('class') # 返回form表单中第一个span标签中的第一个子元素
# 根据索引匹配元素
find_element_by_css_selector('form :nth-child(2)').get_attribute('name') # 返回form表单中第二个input标签
# 匹配兄弟标签
find_element_by_css_selector('form :nth-child(2)+input').get_attribute('name') # 先定位到a,根据a匹配临近的b4.Selenium - webdriver滚动条
4.1 移动到底部或顶部
1 | driver.implicitly_wait(time_to_wait=10) |
4.2 相对移动
1 | driver.execute_script("window.scrollBy(0, 500)") # 相对移动,从当前位置移动500像素 |
4.3 绝对移动
1 | driver.execute_script("window.scrollTo(0, 2200)") # 绝对移动,直接移动到2200像素位置 |
5.Selenium - webdriver鼠标操作
关于鼠标相关操作的方法都封装在ActionChains类中
5.1 基本操作
| Method | Description |
|---|---|
| click(on_element=None) | 鼠标左键单击 |
| click_and_hold(on_element=None) | 鼠标左键单击,但不松开 |
| context_click(on_element=None) | 鼠标右键单击 |
| double_click(on_element=None) | 鼠标左键双击 |
| drag_and_drop(source, target) | 鼠标左键单击不松开,移动到指定元素后松开(即拖拽 ) |
| drag_and_drop_by_offset(source, xoffset, yoffset) | 鼠标左键单击不松开,移动到指定坐标后松开 |
| move_by_offset(xoffset, yoffset) | 鼠标移动到某个坐标 |
| move_to_element(to_element) | 鼠标移动到某个元素 |
| move_to_element_with_offset(to_element, xoffset, yoffset) | 鼠标移动到距离某个元素的某个距离 |
| pause(seconds) | 暂停输入 |
| release(on_element=None) | 在某个元素松开鼠标左键 |
| send_keys(*keys_to_send) | 在当前元素中输入值 |
| send_keys_to_element(element, *keys_to_send) | 给某个元素输入值 |
| perform() | 相应存储的动作 |
| reset_actions() | 清除所有已存储的动作 |
5.2 拖动
1 | # 页面中,有iframe标签,要先切换 |
6.窗口切换
窗口切换一般两种情形
1.是浏览器打开多个窗口,使用selenium在多个窗口中相互切换
2.遇到iframe窗口,相当于嵌套,解决之道 就是切入与切出
6.1 窗口切换
- driver.switch_to系列,切换窗口,包括三个常用的方法:
- switch_to.window,切换窗口,替换
switch_to_window - switch_to.frame,进入iframe,替换
switch_to_frame - switch_to.default_content,退出iframe,替换原方法
switch_to_default_content
- switch_to.window,切换窗口,替换
- window_handles,窗口数组
1 | # 所有打开的窗口都存在这个数组中 |
6.2 iframe窗口切换
- switch_to.frame(iframe),进入窗口
- switch_to.default_content(),退出窗口
7.等待机制
我们可以使用selenium提供的两种等待机制:
显式等待
selenium提供了WebDriverWait类实现等待机制。该类接收4个参数来指定等待机制。1
2
3
4
5
6
7
8
9from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
WebDriverWait(driver=driver, timeout=10, poll_frequency=0.5, ignored_exceptions=None)
# driver,浏览器驱动
# timeout,最长超时时间,单位(秒)
# poll_frequency,轮询检测时间,也就是每隔多少时间检测一次,默认是0.5秒
# ignored_exceptions,超时后的异常信息,默认抛出NoSuchElementExceptionWebDriverWait类提供了两个方法来完成等待机制:- until(self, method, message=’’),method为需要提供的驱动程序,直到返回True,用的较多。
- until_not(self, method, message=’’),method为需要提供的驱动程序,直到返回False
隐式等待
直接通过浏览器驱动对象调用
driver.implicitly_wait(time_to_wait=10) # 只需要一个等待超时时间参数
implicitly_wait等待时间单位为秒,如上例所示,我们指定了10秒。需要注意的是,隐式与显式等待有明显的区别,隐式等待应用于全局,每当使用driver驱动找某个元素时,隐式等待机制就会被触发(导致测试速度变慢),如果元素存在,则继续执行,否则,它将以轮询的方式判断元素是否定位成功,直至等待超时,抛出错误NoSuchElementException。而显式等待则只是指定某(些)个元素是否存在时执行
实际使用:
- 普通(静态页面较多)网页,休眠机制和显式等待机制可以相互搭配,提高效率。
- 动态页面较多的时候,推荐使用隐式等待
8.处理提示框
JavaScript中,关于消息提示框的方法有三个(虽然都跟alert差不多):
- alert(message)方法用于显示带有一条指定消息和一个 OK 按钮的警告框。
- confirm(message)方法用于显示一个带有指定消息和 OK 及取消按钮的对话框。如果用户点击确定按钮,则 confirm() 返回 true。如果点击取消按钮,则 confirm() 返回 false。
- prompt(text,defaultText)方法用于显示可提示用户进行输入的对话框。如果用户单击提示框的取消按钮,则返回 null。如果用户单击确认按钮,则返回输入字段当前显示的文本。
selenium操作上面三种提示框有以下几种方法:
- alertObject.text:获取提示的文本值。
- alertObject.accept():点击『确认』按钮。
- alertObject.dismiss():点击『取消』或者叉掉对话框。
- alertObject.send_keys(message):输入文本,仅适用于prompt方法
处理alert类型与confirm提示框
switch_to.alert方法将webdriver作用域切换到alert提示框上。同时可以使用text获取提示文本信息、accept()点击确认按钮、dismiss()点击取消或者叉掉提示框。
处理prompt提示框
基本步骤不变,但最后可以使用alertObject.send_keys(message)方法
9.规避网站监测
对采取了监测机制的web,只需要设置Chromedriver的启动参数即可解决问题。在启动Chromedriver之前,为Chrome开启实验性功能参数excludeSwitches,它的值为['enable-automation']
1 | from selenium.webdriver import ChromeOptions # 需要导入的类 |
10.遇到链接非私密
添加options参数:
1 | from selenium import webdriver |