文章目录
转载*请注明原始出处:http://blog.youkuaiyun.com/a464057216/article/details/52717464
后续此博客不再更新,欢迎大家搜索关注微信公众号“测开之美”,测试开发工程师技术修炼小站,持续学习持续进步。
简介
Selenium是ThoughtWorks公司为Web自动化测试开发的工具,除支持多种操作系统如Linux、Mac OS X、Windows外,还支持Chrome、Firefox、Safari、Opera、IE等多种浏览器,适合做Web应用的兼容性测试及自动化测试。在Web开发的持续集成中,如果每次迭代都采用手工方式对已有的功能进行回归测试,需要的人力、时间成本将是非常巨大的,采用Selenium进行自动化地回归测试,可以让团队的精力集中在新功能的测试上,提升工作效率。
本文主要介绍在Python中使用Selenium包进行自动化测试的方法,您需要具备一些HTML的知识及搭建Web服务器的方法,这样才能在学习的过程中亲手做实验,另外完整的示例代码放在我的github项目上,欢迎大家访问。首先使用pip install selenium
命令安装Python的Selenium包。针对不同的浏览器及其版本使用Selenium的方法会有所不同,如下是我的版本信息:
- 操作系统:Mac OS Sierra
- Python: 2.7.10
- Selenium: 2.53.6
- Firefox: 47.0.1
- Safari: 10.0
- Opera: 39.0
- Chrome: 53
使用方法
访问页面
Selenium运行自动化测试的方法是打开浏览器,按照脚本规定的步骤模拟人的操作,如点击按钮、在文本框输入文本等,然后检查期望结果。第一步就是要知道访问什么页面,比如测试使用Firefox访问百度:
# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
# 目前支持的driver有Firefox, Chrome, IE和Remote等
driver = webdriver.Firefox(executable_path="./geckodriver")
# 直到页面被加载完(onload事件被触发)才将控制权返回脚本
driver.get('https://www.baidu.com')
assert u'百度一下,你就知道' in driver.title
print u"当前URL:", driver.current_url
对于使用了大量AJAX的页面,webdriver并不知道页面何时真正加载完成,这时需要使用waits告诉Selenium等待时间。
Webdriver是Selenium能够实现跨浏览器自动化测试的关键,通常简称wd。不同浏览器有不同的wd,但对外提供了统一的接口调用各个浏览器自身的自动化测试接口。上面Firefox的webdriver请在此下载。
定位元素
打开页面后,第二件事情就是定位到我们要操作的页面元素,定位单个页面元素有如下方法(下面的方法,如果没有定位到相应的元素,抛出NoSuchElementException
异常):
find_element_by_id
:通过id属性定位元素(返回第一个匹配的)。find_element_by_name
:通过name属性定位元素(返回第一个匹配的)。find_element_by_xpath
:通过xpath定位元素(返回第一个匹配的)。find_element_by_link_text
:通过超链接文本定位超链接元素,必须是完全匹配(返回第一个匹配的)。find_element_by_partial_link_text
:通过超链接文本定位超链接元素,可以是部分匹配(返回第一个匹配的)。find_element_by_tag_name
:通过标签名字定位元素(返回第一个匹配的)。find_element_by_class_name
:通过class属性定位元素(返回第一个匹配的)。find_element_by_css_selector
:使用CSS选择器语法定位元素(返回第一个匹配的)。
上面的方法,对应的批量定位方法如下(返回对应网页元素的列表):
find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
此外,webdriver还有find_element
及find_elements
方法定位元素:
from selenium.webdriver.common.by import By
driver.find_element(By.XPATH, '//button[text()="Some text"]')
driver.find_elements(By.XPATH, '//button')
By对象含有如下属性:
ID
:元素id属性。XPATH
:xpath。LINK_TEXT
:超链接文本。PARTIAL_LINK_TEXT
:部分超链接文本。NAME
:元素name属性。TAG_NAME
:元素标签名字。CLASS_NAME
:元素class属性。CSS_SELECTOR
:使用CSS元素选择器语法。
操作文本框元素
比如在百度的输入框查询信息:
elem = driver.find_element_by_name('wd')
# 清空预填充内容
elem.clear()
# Keys对象表示键盘按键,如F1、ALT、ARROW_DOWN等
elem.send_keys(u'48.HTTP基本认证与摘要认证', Keys.RETURN)
assert u'Mars Loo的博客' in driver.page_source
# quit方法关闭整个浏览器
driver.quit()
上传文件也可以采用send_keys
方法,此时参数提供为文件的路径即可。
操作表单元素
定位到一个表单元素后,可以调用其submit
方法提交表单:
form = ff.find_element_by_name('survey')
form.submit()
如果调用submit
方法的元素不是表单,抛出异常NoSuchElementException
。提交表单也可以定位到提交按钮元素后,调用其click
方法:
submit = ff.find_element_by_id('submit')
submit.click()
操作select元素
# 采用xpath获取第一个select元素(即使有多个也返回第一个)
element = ff.find_element_by_xpath("//select[@name='car']")
# 获取所有选项,打印选项值并点击
all_options = element.find_elements_by_tag_name("option")
for option in all_options:
print "Value is: %s" % option.get_attribute("value")
option.click()
Selenium还提供了对<select>
元素的抽象——Select对象:
from selenium.webdriver.support.ui import Select
select = Select(ff.find_element_by_xpath("//select[@name='car']"))
select.select_by_visible_text("infinity")
select.select_by_value("bmw")
# 按照option的索引选择,第一个option的index为0
select.select_by_index("0")
对于允许多选以及有预选值的场景,可以做如下处理:
select = Select(ff.find_element_by_xpath("//select[@name='car']"))
# 获取所有已选项
print select.all_selected_options
# 去选所有选项
select.deselect_all()
# 获取所有可选项
print select.options
拖放元素
selenium包目前还不支持HTML 5的拖放,一种解决方案是通过Javascript脚本及jQuery解决:
jquery_url = "http://upcdn.b0.upaiyun.com/libs/jquery/jquery-2.0.2.min.js"
ff = webdriver.Firefox(executable_path="./geckodriver")
ff.get('http://html5demos.com/drag')
ff.set_script_timeout(30)
with open("load_jquery.js") as f:
load_jquery_js = f.read()
with open("drag_and_drop.js") as f:
drag_and_drop_js = f.read()
ff.execute_async_script(load_jquery_js, jquery_url)
ff.execute_script(drag_and_drop_js +
"$('#one').simulateDragDrop({dropTarget:'#bin'});")
load_jquery.js
和drag_and_drop.js
请在我的github项目获取。
在窗口和frame间切换
在浏览器不同窗口之间切换的方法如下:
a = ff.find_element_by_tag_name("a")
# 保存原始窗口,window_handlers是目前wd打开的所有窗口的句柄列表
prev = ff.window_handles[-1]
# 点击超链接(targe="_blank")后,浏览器新窗口被激活
a.click()
# 保存新窗口
new = ff.window_handles[-1]
# 切换到原始窗口
ff.switch_to_window(prev)
print "Switch to prev success"
# 切换到新窗口
ff.switch_to_window(new)
print "Switch to new success"
因为HTML 5中不再建议使用<frameset>
标签,所以以<iframe>
为例说明如何在框架间切换:
ff.switch_to_frame('frame1')
element = ff.find_element_by_name('wd')
element.clear()
element.send_keys("This is a test")
# 切换到顶级frame,然后才可以切换到其他iframe
ff.switch_to_default_content()
ff.switch_to_frame('frame2')
element = ff.find_element_by_name('email')
element.clear()
element.send_keys("Input to Sohu")
处理弹窗
从Javascript的角度,弹窗分为三种:alert、confirm和prompt。使用webdriver的switch_to_alert
方法可以将焦点切换到当前的弹窗上,并返回一个Alert
对象。
如果是alert类型的窗口,点击取消
或者确定
都可以关闭该窗口:
# 切换到当前弹出框并返回Alert对象
alert = ff.switch_to_alert()
# 获取弹出框的文本内容
print "Alert text:", alert.text
# 点击弹出框的确定按钮
alert.accept()
# 点击弹出框的取消按钮
# alert.dismiss()
如果是confirm类型的窗口,点击确定
会给Javascript返回true
,点击取消
会给javascript返回false
。如果是prompt类型的窗口,可以输入一段文本后再点击取消
还是确定
,如果点击取消
会给Javascript返回null
,如果点击确定
会给Javascript返回输入的文本,因为弹出的prompt框默认会将其中可编辑的文本内容覆盖选中(见下图),所以Alert对象没有clear
方法:
alert = ff.switch_to_alert()
# 如果是prompt类型的弹出框,直接向其中输入内容
alert.send_keys("This is my choice")
# 然后点击prompt框的确定按钮
alert.accept()
处理HTTP基本认证及摘要认证
HTTP基本认证和摘要认证的基本原理可以参考我的博文,这种认证弹窗不需要按照上面的方法处理,如果是Firefox浏览器,可以在浏览器中访问about:config
进入配置界面将browser.safebrowsing.malware.enabled
设置为true
,此时Firefox就允许我们以scheme://username:password@domain/path?query#segment
的形式访问网页了。设置好了以后,Selenium的脚本可以这样写:
ff = webdriver.Firefox(executable_path="./geckodriver")
ff.get('http://mars:loo@localhost:5000/')
历史记录前进及后退
通过webdriver的back
和forward
方法,可以很方便地在历史记录中前进及后退:
a = ff.find_element_by_tag_name('a')
a.click()
print "Go back"
ff.back()
print "Go forward"
ff.forward()
cookie
处理cookie时,需要webdriver先访问到合法的cookie域,添加cookie然后重新访问即可,比如(name
和value
字段分别表示cookie的键和值):
ff.get('http://localhost:5000/')
ff.add_cookie({'name': 'username', 'value': 'marsloo'})
ff.get('http://localhost:5000/')
# 获取当前域的cookies
print ff.get_cookies()
上述代码的输出可能为:
[{u'domain': u'localhost', u'name': u'username', u'value': u'marsloo', u'expiry': None, u'path': u'', u'httpOnly': False, u'secure': False}]
等待元素加载
如果请求的页面使用了大量的AJAX,Selenium不会等待其请求完成后再将控制权交给脚本。为了正确定位到所需的元素,需要使用Selenium的等待功能,最简单的是这样:
# 对于所有元素获取,会等待3秒
ff.implicitly_wait(3)
try:
a = ff.find_element_by_partial_link_text('g')
a.click()
except NoSuchElementException:
print "Page load fail or no such element"
在每一个浏览器会话中只需要调用一次implicitly_wait
方法。如果相对每个元素获取设置不同的超时时间,可以这样做(其他更详细的代码可以在我的github项目获取):
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
@auto_close
def func():
try:
a = WebDriverWait(ff, 3).until(
EC.presence_of_element_located((By.TAG_NAME, "p")))
a.click()
except TimeoutException:
print "No such element"
上述代码中的(By.TAG_NAME, "p")
元组,在下面的解释中以locator
参数代替。expected_conditions
对象还有很多方法可以使用:
title_is(title)
:标题是title
(完全匹配)。title_contains(title)
:标题包含title
(部分匹配)。presence_of_element_located(locator)
:元素在DOM树上存在(但是不一定可见)。visibility_of_element_located(locator)
:元素在DOM树上存在并且肉眼可见。visibility_of(element)
:检查元素的肉眼可见性。presence_of_all_elements_located(locator)
:元素们在DOM树上存在(但是不一定可见),locator
返回的是元素的列表。text_to_be_present_in_element(locator, text)
:某个元素的文本中是否包含text
。text_to_be_present_in_element_value(locator, text)
:某个元素的value
属性中是否包含text
。frame_to_be_available_and_switch_to_it(locator)
:判断某个frame是否可操作,如果可以的话切换到该frame并返回True
,否则返回False
。invisibility_of_element_located(locator)
:元素不存在于DOM树或者不可见。element_to_be_clickable(locator)
:元素可见且是可点击状态。staleness_of(element)
:元素从DOM树上移除。element_to_be_selected(element)
:元素被选中。element_located_to_be_selected(locator)
:元素被选中。element_selection_state_to_be(element, bool)
:判断元素被选中的状体,bool
参数中True
表示选中,False
表示未选中。element_located_selection_state_to_be(locator, bool)
:判断元素被选中的状体,bool
参数中True
表示选中,False
表示未选中。alert_is_present
:弹出了一个窗口。
使用其他浏览器
Chrome
使用Chrome进行测试,需要先下载Chrome Driver (可能需要翻墙),解压缩后代码如下:
driver = webdriver.Chrome(executable_path="./chromedriver")
Opera
首先下载Opera Driver,解压缩后代码如下:
webdriver_service = service.Service('./operadriver')
webdriver_service.start()
driver = webdriver.Remote(webdriver_service.service_url,
webdriver.DesiredCapabilities.OPERA)
driver.get('https://www.baidu.com')
Safari
Safari 10已经开始内置自动化测试的支持,Safari偏好设置
->高级
->在菜单栏中显示“开发”菜单
,然后开发
->允许远程自动化
,最后下载Selenium Server的jar包,测试脚本如下:
# -*- coding: utf-8 -*-
from selenium import webdriver
import time
import os
os.environ["SELENIUM_SERVER_JAR"] = "selenium-server-standalone-2.53.1.jar"
# 设置quiet模式,否则会打印很多log
driver = webdriver.Safari(quiet=True)
driver.get('https://www.baidu.com')
assert u'百度一下,你就知道' in driver.title
print u"当前URL:", driver.current_url
time.sleep(4)
driver.quit()
保存屏幕截图
使用driver.save_screenshot(filename)
可以保存浏览器的运行截图(默认是覆盖写入)。
滚动至屏幕底部
使用ff.execute_script("window.scrollTo(0, document.body.scrollHeight);")
可以滚动至屏幕底部。
如果觉得我的文章对您有帮助,欢迎关注我(优快云:Mars Loo的博客)或者为这篇文章点赞,谢谢!