首页 技术 正文
技术 2022年11月16日
0 收藏 505 点赞 4,121 浏览 85697 个字

阅在线 AIP 文档:
http://selenium.googlecode.com/git/docs/api/py/index.html

目录
一、selenium+python 环境搭建……………………………………………………………………………………6
1.1 selenium 介绍………………………………………………………………………………………………… 6
1.2 准备工作……………………………………………………………………………………………………….6
1.3 安装步骤……………………………………………………………………………………………………….7
1.4 安装chrome driver…………………………………………………………………………………………8
1.5 安装 IE driver……………………………………………………………………..9
二、开始第一个脚本…………………………………………………………………………………………………..9
2.1 为什么选python…………………………………………………………………………………………….9
2.2 第一个脚本……………………………………………………………………………………………………9
2.3 脚本解析……………………………………………………………………………………………………..10
三、元素的定位………………………………………………………………………………………………………..11
3.1 id 和 name 定位………………………………………………………………………………………….12
3.2 tagname 和 class name定位………………………………………………………………………..12
3.3 CSS 定位……………………………………………………………………………………………………13
3.4 XPath 定位…………………………………………………………………………………………………14
3.5 link 定位…………………………………………………………………………………………………….15
3.6 Partial link text 定位……………………………………………………………………………………15
四、添加等待时间…………………………………………………………………………………………………….15
4.1、添加休眠……………………………………………………………………………………………………15
4.2、智能等待……………………………………………………………………………………………………16
五、打印信息……………………………………………………………………………………………………………17
5.1、打印 tile……………………………………………………………………………………………………..17
5.2、打印 URL…………………………………………………………………………………………………..17
六、浏览器的操作…………………………………………………………………………………………………….18
6.1、浏览器最大化…………………………………………………………………………………………….18
6.2、设置浏览器宽、高……………………………………………………………………………………..19
七、操作浏览器的前进、后退…………………………………………………………………………………..19
八、操作测试对象…………………………………………………………………………………………………….20
8.1、鼠标点击与键盘输入………………………………………………………………………………….21
8.2、submit 提交表单…………………………………………………………………………………………21
8.3、text获取元素文本………………………………………………………………………………………22
8.4、get_attribute 获得属性值……………………………………………………………………………. 22
九、键盘事件……………………………………………………………………………………………………………23
9.1、键盘按键用法…………………………………………………………………………………………….23
9.2、键盘组合键用法…………………………………………………………………………………………24
9.3、中文乱码问题…………………………………………………………………………………………….25
十、鼠标事件……………………………………………………………………………………………………………25
10.1、鼠标右键………………………………………………………………………………………………….26
博客园 — 虫师
http://fnng.cnblogs.com 4
10.2、鼠标双击………………………………………………………………………………………………….27
10.3、鼠标拖放………………………………………………………………………………………………….27
十一、定位一组元素…………………………………………………………………………………………………28
11.1、第一种定位方法……………………………………………………………………………………….30
11.2、第二种定位方法……………………………………………………………………………………….31
11.3、去掉最后一个勾选……………………………………………………………………………………31
十二、多层框架/窗口定位…………………………………………………………………………………………32
12.1、多层框架定位…………………………………………………………………………………………..32
12.2、多层窗口定位…………………………………………………………………………………………..35
十三、层级定位………………………………………………………………………………………………………..35
十四、上传文件操作…………………………………………………………………………………………………38
14.1、操作文件上传例子……………………………………………………………………………………39
14.2、139 邮箱上传……………………………………………………………………………………………40
十五、下拉框处理…………………………………………………………………………………………………….41
15.1、操作下拉框例子……………………………………………………………………………………….41
15.2、百度搜索设置下拉框操作…………………………………………………………………………43
十六、alert、confirm、prompt 的处理……………………………………………………………………….44
十七、对话框的处理…………………………………………………………………………………………………45
17.1、div 对话框的处理……………………………………………………………………………………..45
17.2、一般对话框的处理……………………………………………………………………………………48
十八、调用 js……………………………………………………………………………………………………………49
18.1、通过 js 隐藏元素………………………………………………………………………………………49
18.2、通过 js 使输入框标红……………………………………………………………………………….51
十九、控制浏览器滚动条………………………………………………………………………………………….52
19.1、场景一……………………………………………………………………………………………………..53
19.2、场景二……………………………………………………………………………………………………..53
二十、cookie 处理…………………………………………………………………………………………………….54
20.1、打印 cookie 信息………………………………………………………………………………………54
20.2、对cookie 操作………………………………………………………………………………………….55
20.3、博客园登陆分析cookie…………………………………………………………………………….56
二十一、webdriver 原理解析……………………………………………………………………………………. 57
二十二、引入 unittest 框架………………………………………………………………………………………..65
二十三、unittest 单元测试框架解析…………………………………………………………………………..70
二十四、批量执行测试集………………………………………………………………………………………….75
二十五、异常捕捉与错误截图…………………………………………………………………………………..77
二十六、生成测试报告(HTMLTestRunner)……………………………………………………………….. 80
二十七、数据驱动测试……………………………………………………………………………………………..83
27.1、读取文件参数化……………………………………………………………………………………….83
27.2、用户名密码的参数化(读取文件)…………………………………………………………. 85
27.3、用户名的参数化(字典)…………………………………………………………………………86
27.4、用户名密码的参数化(函数)………………………………………………………………… 87
二十八、测试套件…………………………………………………………………………………………………….89
28.1、测试套件实例…………………………………………………………………………………………..89
28.2、整合 HTMLTestRunner 测试报告………………………………………………………………93
博客园 — 虫师
http://fnng.cnblogs.com 5
28.3、更易读的报告…………………………………………………………………………………………..95
二十九、结构改进…………………………………………………………………………………………………….96
29.1、all_tests.py 移出来……………………………………………………………………………………96
29.2、__init__.py 文件解析………………………………………………………………………………..97
29.3、调用多级目录的用例………………………………………………………………………………..98
29.4、改进用例的读取……………………………………………………………………………………….99
29.5、进一步分离用例列表………………………………………………………………………………101
三十、UliPad–python 开发利器……………………………………………………………………………….103

一、selenium+python  环境搭建
1.1selenium  介绍
selenium 是一个 web 的自动化测试工具,不少学习功能自动化的同学开始首选 selenium ,
相因为它相比 QTP有诸多有点:
* 免费,也不用再为破解 QTP 而大伤脑筋
* 小巧,对于不同的语言它只是一个包而已,而 QTP 需要下载安装1个多 G 的程序。
* 这也是最重要的一点,不管你以前更熟悉 C、 java、ruby、python、或都是 C# ,你都
可以通过 selenium 完成自动化测试,而 QTP 只支持 VBS
* 支持多平台:windows、linux、MAC ,支持多浏览器:ie、ff、safari、opera、chrome
* 支持分布式测试用例的执行,可以把测试用例分布到不同的测试机器的执行,相当于分
发机的功能。
1.2  准备工作
搭建平台 windows
准备工具如下:
————————————————————-
下载 python
http://python.org/getit/
下载 setuptools 【python的基础包工具】
http://pypi.python.org/pypi/setuptools
下载 pip 【python 的安装包管理工具】
https://pypi.python.org/pypi/pip
————————————————————-
因为版本都在更新,pyhton 选择2.7.xx ,setuptools 选择你平台对应的版本,pip 不要
担心 tar.gz 在 windows 下一样可用。

1.3  安装步骤
一、python 的安装 ,这个不解释,exe 文件运行安装即可,既然你选择 python,相信
你是熟悉 python 的,我安装目录 C:\Python27
二、setuptools 的安装也非常简单,同样是 exe 文件,默认会找到 python 的安装路径,
将安装到 C:\Python27\Lib\site-packages 目录下
三、安装 pip ,我默认解压在了 C:\pip-1.3.1 目录下
四、打开命令提示符(开始—cmd 回车)进入 C:\pip-1.3.1目录下输入:
C:\pip-1.3.1 >python setup.pyinstall
(如果提示 python 不是内部或外部命令!别急,去配置一下环境变量吧)
修改我的电脑->属性->高级->环境变量->系统变量中的 PATH 为:
变量名:PATH
变量值:;C:\Python27
五、再切换到 C:\Python27\Scripts 目录下输入:
C:\Python27\Scripts> easy_install pip
六、安装 selenium,(下载地址: https://pypi.python.org/pypi/selenium )
如果是联网状态的话,可以直接在 C:\Python27\Scripts 下输入命令安装:
C:\Python27\Scripts> pip install -U selenium
如果没联网(这个一般不太可能),下载 selenium 2.33.0 (目前的最新版本)
并解压把整个目录放到 C:\Python27\Lib\site-packages 目录下。
注意:七、八两步可以暂不进行,如果你要学习第二十一章 webdriver 原理的时候再进行
也不迟。
==============
七、下载并安装
(http://www.java.com/zh_CN/download/chrome.jsp?locale=zh_CN),什么!?你没整过
java 虚拟机,百度一下 java 环境搭建吧。
八、下载 selenium 的服务端(https://code.google.com/p/selenium/)在页面的左
侧列表中找到
selenium-server-standalone-XXX.jar
对!就是这个东西,把它下载下来并解压;
在 selenium-server-standalone-xxx.jar 目 录 下 使 用 命 令 java -jar
selenium-server-standalone-xxx.jar 启动(如果打不开,查看是否端口被占用:netstat
-aon|findstr 4444)。
==============
1.4  安装 chrome driver
chrome driver 的下载地址在这里。
1. 下载解压,你会得到一个 chromedriver.exe 文件(我点开,运行提示 started no prot
9515 ,这是干嘛的?端口9515被占了?中间折腾了半天),后来才知道需要把这家伙放到
chrome 的安装目录下…\Google\Chrome\Application\ ,然后设置 path 环境变量,把
chrome 的安装目录(我的:C:\Program Files\Google\Chrome\Application),然后再调用
运行:
# coding = utf-8
fromselenium import webdriver
driver =webdriver.Chrome()
driver.get(‘http://radar.kuaibo.com’)
print driver.title
driver.quit()
报错提示:
Chrome version must be >= 27.0.1453.0\n (Driver info:
chromedriver=2.0,platform=Windows NT 5.1 SP3 x86)
说我 chrome 的版本没有大于27.0.1453.0 ,这个好办,更新到最新版本即可。
博客园 — 虫师
http://fnng.cnblogs.com 9
1.5  安装IE driver
在新版本的 webdriver中,只有安装了 ie driver使用 ie 进行测试工作。
ie driver 的下载地址在这里,记得根据自己机器的操作系统版本来下载相应的 driver。
暂时还没尝试,应该和 chrome 的安装方式类似。
记得配置 IE 的保护模式
如果要使用 webdriver启动 IE 的话,那么就需要配置 IE 的保护模式了。
把 IE 里的保护模式都选上或都勾掉就可以了。
二、开始第一个脚本
2.1  为什么选 python
之前的菜鸟系列是基于 java 的,一年没学其实也忘的差不多了,目前所测的产品
部分也是 python 写的,而且团队也在推广 python ,其实就测试人员来说,python 也
相当受欢迎。易学,易用。翻翻各测试招聘,python 出现的概率也颇高。(个人原因)
最重要的还是 python 简单易学,应用也相对广泛;是测试人员学习编程的不二之选。
下面看看 python 穿上 seleniumwebdriver 是多么的性感:
2.2  第一个脚本
#coding = utf-8
from selenium import webdriver
browser = webdriver.Firefox()
browser.get(“http://www.baidu.com”)
browser.find_element_by_id(“kw” ).send_keys( “selenium”)
browser.find_element_by_id(“su”).click()
博客园 — 虫师
http://fnng.cnblogs.com 10
browser.quit()
2.3  脚本解析
# coding = utf-8
可加可不加,开发人员喜欢加一下,防止乱码嘛。
from selenium import webdriver
要想使用 selenium 的 webdriver里的函数,首先把包导进来嘛
browser = webdriver.Firefox()
我们需要操控哪个浏览器呢?Firefox ,当然也可以换成 Ie 或 Chrome 。browser 可以
随便取,但后面要用它操纵各种函数执行。
browser.find_element_by_id(“kw”).send_keys(“selenium”)
一个控件有若干属性 id 、name、(也可以用其它方式定位),百度输入框的 id 叫 kw ,
我要在输入框里输入 selenium 。多自然语言呀!
browser.find_element_by_id(“su”).click()
搜索的按钮的 id 叫 su ,我需要点一下按钮( click() )。
browser.quit()
退出并关闭窗口的每一个相关的驱动程序,有洁癖用这个。
browser.close()
关闭当前窗口 ,用哪个看你的需求了。
博客园 — 虫师
http://fnng.cnblogs.com 11
三、元素的定位
对象的定位应该是自动化测试的核心,要想操作一个对象,首先应该识别这个对象。
一个对象就是一个人一样,他会有各种的特征(属性),如比我们可以通过一个人的身
份证号,姓名,或者他住在哪个街道、楼层、门牌找到这个人。
那么一个对象也有类似的属性,我们可以通过这个属性找到这对象。
webdriver 提供了一系列的对象定位方法,常用的有以下几种
 · id
 · name
 · class name
 · link text
 · partial link text
 · tag name
 · xpath
 · css selector
我们可以看到,一个百度的输入框,可以用这么用种方式去定位。
<input id=”kw” class=”s_ipt” type=”text” maxlength=”100″ name=”wd”
autocomplete=”off”>
#coding=utf-8
fromselenium import webdriver
browser =webdriver.Firefox()
browser.get(“http://www.baidu.com”)
#########百度输入框的定位方式##########
#通过 id 方式定位
browser.find_element_by_id(“kw”).send_keys(“selenium”)
#通过 name 方式定位
browser.find_element_by_name(“wd”).send_keys(“selenium”)
#通过 tag name 方式定位
browser.find_element_by_tag_name(“input”).send_keys(“selenium”)
#通过 class name 方式定位
browser.find_element_by_class_name(“s_ipt”).send_keys(“selenium”)
博客园 — 虫师
http://fnng.cnblogs.com 12
#通过 CSS 方式定位
browser.find_element_by_css_selector(“#kw”).send_keys(“selenium”)
#通过 xphan 方式定位
browser.find_element_by_xpath(“//input[@id=’kw’]”).send_keys(“selenium”)
############################################
browser.find_element_by_id(“su”).click()
time.sleep(3)
browser.quit()
3.1 id 和 和 name  定位
id 和 name 是我们最最常用的定位方式,因为大多数控件都有这两个属性,而且
在对控件的 id 和 name 命名时一般使其有意义也会取不同的名字。通过这两个属性使我
们找一个页面上的属性变得相当容易
我们通过前端工具,找到了百度输入框的属性信息,如下:
<input id=”kw” class=”s_ipt” type=”text” maxlength=”100″ name=”wd”
autocomplete=”off”>
id=”kw”
通过 find_element_by_id(“kw”) 函数就是捕获到百度输入框
name=”wd”
通过 find_element_by_name(“wd”)函数同样也可以捕获百度输入框
3.2 tag name 和 和 class name  定位
从上面的百度输入框的属性信息中,我们看到,不单单只有 id 和 name 两个属性,
比如 class 和 tag name(标签名)
<input id=”kw” class=”s_ipt” type=”text” maxlength=”100″ name=”wd”
autocomplete=”off”>
<input>
博客园 — 虫师
http://fnng.cnblogs.com 13
input就是一个标签的名字,可以通过 find_element_by_tag_name(“input”) 函数来定
位。
class=”s_ipt”
通过 find_element_by_class_name(“s_ipt”)函数捕获百度输入框。
3.3 CSS  定位
CSS(Cascading Style Sheets)是一种语言,它被用来描述 HTML 和 XML 文档的表现。
CSS 使用选择器来为页面元素绑定属性。这些选择器可以被 selenium 用作另外的定位策
略。
CSS 的比较灵活可以选择控件的任意属性,上面的例子中:
find_element_by_css_selector(“#kw”)
通过 find_element_by_css_selector( )函数,选择取百度输入框的id 属性来定义
也可以取 name 属性
<a href=”http://news.baidu.com” name=”tj_news”>新 闻</a>
driver.find_element_by_css_selector(“a[name=\”tj_news\”]”).click()
可以取 title属性
<a onclick=”queryTab(this);” mon=”col=502&pn=0″ title=”web”
href=”http://www.baidu.com/”>网页</a>
driver.find_element_by_css_selector(“a[title=\”web\”]”).click()
也可以是取..:
<a class=”RecycleBin xz” href=”javascript:void(0);”>
driver.find_element_by_css_selector(“a.RecycleBin”).click()
虽然我也没全部理解 CSS 的定位,但是看上去应该是一种非常灵活和牛 X 的定位方式
扩展阅读:
http://www.w3.org/TR/css3-selectors/
博客园 — 虫师
http://fnng.cnblogs.com 14
http://www.w3school.com.cn/css/css_positioning.asp
3.4 XPath  定位
什么是 XPath:http://www.w3.org/TR/xpath/
XPath 基础教程:http://www.w3schools.com/xpath/default.asp
selenium 中被误解的 XPath : http://magustest.com/blog/category/webdriver/
XPath 是一种在 XML 文档中定位元素的语言。因为 HTML 可以看做 XML 的一种实现,
所以 selenium 用户可是使用这种强大语言在 web 应用中定位元素。
XPath 扩展了上面 id和 name 定位方式,提供了很多种可能性,比如定位页面上的
第三个多选框。
xpath:attributer (属性)
driver.find_element_by_xpath(“//input[@id=’kw’]”).send_keys(“selenium”)
#input 标签下 id =kw 的元素
xpath:idRelative (id 相关性)
driver.find_element_by_xpath(“//div[@id=’fm’]/form/span/input”).send_keys(“s
elenium”)
#在/form/span/input 层级标签下有个 div标签的 id=fm 的元素
driver.find_element_by_xpath(“//tr[@id=’check’]/td[2]”).click()
# id 为’check’ 的 tr ,定位它里面的第2个 td
xpath:position (位置)
driver.find_element_by_xpath(“//input”).send_keys(“selenium”)
driver.find_element_by_xpath(“//tr[7]/td[2]”).click()
#第7个 tr 里面的第2个td
xpath: href (水平参考)
driver.find_element_by_xpath(“//a[contains(text(),’网页’)]”).click()
#在 a 标签下有个文本(text)包含(contains)’网页’ 的元素
xpath:link
driver.find_element_by_xpath(“//a[@href=’http://www.baidu.com/’]”).click()
#有个叫 a 的标签,他有个链接 href=’http://www.baidu.com/ 的元素
博客园 — 虫师
http://fnng.cnblogs.com 15
3.5 link  定位
有时候不是一个输入框也不是一个按钮,而是一个文字链接,我们可以通过 link
#coding=utf-8
fromselenium import webdriver
browser =webdriver.Firefox()
browser.get(“http://www.baidu.com”)
browser.find_element_by_link_text(“贴 吧”).click()
browser.quit()
一般一个页面上不会出现相同的文件链接,通过文字链接来定位也是一种简单有效
的定位方式。
3.6 Partiallink text  定位
通过部分链接定位,这个有时候也会用到,我还没有想到很好的用处。拿上面的例
子,我可以只用链接的一部分文字进行匹配:
browser.find_element_by_partial_link_text(“贴”).click()
#通过 find_element_by_partial_link_text() 函数,我只用了“贴”字,脚本一样找到了”贴 吧
” 的链接
四、添加等待时间
有时候为了保证脚本运行的稳定性,需要脚本中添加等待时间。
4.1 、添加休眠
添加休眠非常简单,我们需要引入 time 包,就可以在脚本中自由的添加休眠时间了。
# coding = utf-8
博客园 — 虫师
http://fnng.cnblogs.com 16
fromselenium import webdriver
import time #调入 time 函数
browser =webdriver.Firefox()
browser.get(“http://www.baidu.com”)
time.sleep(0.3) #休眠0.3秒
browser.find_element_by_id(“kw”).send_keys(“selenium”)
browser.find_element_by_id(“su”).click()
time.sleep(3) # 休眠3秒
browser.quit()
4.2 、智能等待
通过添加 implicitly_wait() 方法就可以方便的实现智能等待;implicitly_wait(30)
的用法应该比 time.sleep() 更智能,后者只能选择一个固定的时间的等待,前者可以
在一个时间范围内智能的等待。
文档解释:
selenium.webdriver.remote.webdriver.implicitly_wait(time_to_wait)
隐式地等待一个无素被发现或一个命令完成;这个方法每次会话只需要调用一次
time_to_wait: 等待时间
用法:
browser.implicitly_wait(30)
# coding = utf-8
fromselenium import webdriver
import time #调入 time 函数
browser =webdriver.Firefox()
browser.get(“http://www.baidu.com”)
browser.implicitly_wait(30) #智能等待30秒
browser.find_element_by_id(“kw”).send_keys(“selenium”)
browser.find_element_by_id(“su”).click()
browser.quit()
博客园 — 虫师
http://fnng.cnblogs.com 17
五、打印信息
很多时间我们不可能盯着脚本执行,我们需要一些打印信息来证明脚本运行是否正确:
5.1 、打印 tile
把刚才访问页面的 title 打印出来。
coding = utf-8
fromselenium import webdriver
driver = webdriver.Chrome()
driver.get(‘http://www.baidu.com’)
print driver.title # 把页面 title 打印出来
driver.quit()
虽然我没看到脚本的执行过程,但我在执行结果里看到了
>>>
百度一下,你就知道
说明页面正确被我打开了。
5.2 、打印 URL
可以将浏览器的 title 打印出来,这里再讲个简单的,把当前 URL 打印出来。其实
也没啥大用,可以做个凑数的用例。
#coding=utf-8
fromselenium import webdriver
import time
browser =webdriver.Firefox()
url=’http://www.baidu.com’
博客园 — 虫师
http://fnng.cnblogs.com 18
#通过 get 方法获取当前 URL 打印
print “now access %s” %(url)
browser.get(url)
time.sleep(2)
browser.find_element_by_id(“kw”).send_keys(“selenium”)
browser.find_element_by_id(“su”).click()
time.sleep(3)
browser.quit()
六、浏览器的操作
6.1 、浏览器最大化
我们知道调用启动的浏览器不是全屏的,这样不会影响脚本的执行,但是有时候会
影响我们“观看”脚本的执行。
#coding=utf-8
fromselenium import webdriver
import time
browser =webdriver.Firefox()
browser.get(“http://www.baidu.com”)
print “浏览器最大化”
browser.maximize_window() #将浏览器最大化显示
time.sleep(2)
browser.find_element_by_id(“kw”).send_keys(“selenium”)
browser.find_element_by_id(“su”).click()
time.sleep(3)
browser.quit()
博客园 — 虫师
http://fnng.cnblogs.com 19
6.2 、设置浏览器宽、高
最大化还是不够灵活,能不能随意的设置浏览的宽、高显示?当然是可以的。
#coding=utf-8
from selenium import webdriver
import time
browser =webdriver.Firefox()
browser.get(“http://m.mail.10086.cn”)
time.sleep(2)
#参数数字为像素点
print “设置浏览器宽480、高800显示”
browser.set_window_size(480, 800) time.sleep(3)
browser.quit()
七、操作浏览器的前进、后退
浏览器上有一个后退、前进按钮,对于浏览网页的人是比较方便的;对于做 web 自
动化测试的同学来说应该算是一个比较难模拟的问题;其实很简单,下面看看 python
的实现方式。
#coding=utf-8
fromselenium import webdriver
import time
browser =webdriver.Firefox()
#访问百度首页
first_url= ‘http://www.baidu.com’
博客园 — 虫师
http://fnng.cnblogs.com 20
print “now access %s” %(first_url)
browser.get(first_url)
time.sleep(2)
#访问新闻页面
second_url=’http://news.baidu.com’
print “now access %s” %(second_url)
browser.get(second_url)
time.sleep(2)
#返回(后退)到百度首页
print “back to %s “%(first_url)
browser.back()
time.sleep(1)
#前进到新闻页
print “forwardto %s”%(second_url)
browser.forward()
time.sleep(2)
browser.quit()
为了使过程让你看得更清晰,在每一步操作上都加了 print和 sleep 。
说实话,这两个功能平时不太常用,所能想到的场景就是几个页面来回跳转,但又
不想用 get url 的情况下。
八、操作测试对象
前面讲到了不少知识都是定位元素,定位只是第一步,定位之后需要对这个原素进
行操作。鼠标点击呢还是键盘输入,这要取决于我们定位的是按钮还输入框。
博客园 — 虫师
http://fnng.cnblogs.com 21
一般来说,webdriver 中比较常用的操作对象的方法有下面几个
 click 点击对象
 send_keys 在对象上模拟按键输入
 clear 清除对象的内容,如果可以的话
 submit 清除对象的内容,如果可以的话
 text 用于获取元素的文本信息
8.1 、鼠标点击与键盘输入
在我们本系列开篇的第一个例子里就用到了到 click 和 send_skys ,别翻回去找
了,我再贴一下代码:
coding=utf-8
fromselenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(“http://www.baidu.com”)
driver.find_element_by_id(“kw”).clear()
driver.find_element_by_id(“kw”).send_keys(“selenium”)
time.sleep(2)
#通过 submit() 来操作
driver.find_element_by_id(“su”).submit()
time.sleep(3)
driver.quit()
send_keys(“xx”) 用于在一个输入框里输入 xx 内容。
click() 用于点击一个按钮。
clear() 用于清除输入框的内容,比如百度输入框里默认有个“请输入关键
字”的信息,再比如我们的登陆框一般默认会有“账号”“密码”这样的默认信息。
clear 可以帮助我们清除这些信息。
8.2 、submit  提交表单
我们把“百度一下”的操作从 click 换成 submit 可以达到相同的效果:
#coding=utf-8
fromselenium import webdriver
import time
博客园 — 虫师
http://fnng.cnblogs.com 22
driver = webdriver.Firefox()
driver.get(“http://www.baidu.com”)
driver.find_element_by_id(“kw”).send_keys(“selenium”)
time.sleep(2)
#通过 submit() 来操作
driver.find_element_by_id(“su”).submit()
time.sleep(3)
driver.quit()
8.3 、text  获取元素文本
text 用于获取元素的文本信息
下面把百度首页底部的声明打印输出
#coding=utf-8
fromselenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(“http://www.baidu.com”)
time.sleep(2)
#id = cp 元素的文本信息
data=driver.find_element_by_id(“cp”).text
print data #打印信息
time.sleep(3)
driver.quit()
输出:
>>>
©2013 Baidu使用百度前必读 京 ICP 证030173号
8.4 、get_attribute  获得属性值
get_attribute
博客园 — 虫师
http://fnng.cnblogs.com 23
获得属性值。
这个函数的用法前面已经有出现过,在定位一组元素的时候有使用到它,只是我们
没有做过多的解释。
一般用法:
select = driver.find_element_by_tag_name(“select”)
allOptions = select.find_elements_by_tag_name(“option”)
for option in allOptions:
print “Value is: ” + option.get_attribute(“value”)
option.click()
…..
具体应用参考第十一节 层级定位例子。
九、键盘事件
本章重点:
 键盘按键用法
 键盘组合键用法
 send_keys()输入中文乱码问题
9.1 、键盘按键用法
#coding=utf-8
fromselenium import webdriver
fromselenium.webdriver.common.keys import Keys #需要引入 keys 包
import os,time
driver = webdriver.Firefox()
driver.get(“http://passport.kuaibo.com/login/?referrer=http%3A%2F%2Fwebcloud
.kuaibo.com%2F”)
博客园 — 虫师
http://fnng.cnblogs.com 24
time.sleep(3)
driver.maximize_window()# 浏览器全屏显示
driver.find_element_by_id(“user_name”).clear()
driver.find_element_by_id(“user_name”).send_keys(“fnngj”)
#tab 的定位相相于清除了密码框的默认提示信息,等同上面的 clear()
driver.find_element_by_id(“user_name”).send_keys(Keys.TAB)
time.sleep(3)
driver.find_element_by_id(“user_pwd”).send_keys(“123456”)
#通过定位密码框,enter(回车)来代替登陆按钮
driver.find_element_by_id(“user_pwd”).send_keys(Keys.ENTER)
”’
#也可定位登陆按钮,通过 enter(回车)代替 click()
driver.find_element_by_id(“login”).send_keys(Keys.ENTER)
”’
time.sleep(3)
driver.quit()
要想调用键盘按键操作需要引入keys 包:
fromselenium.webdriver.common.keysimportKeys
通过send_keys()调用按键:
send_keys(Keys.TAB) #TAB
send_keys(Keys.ENTER) #回车
注意:这个操作和页面元素的遍历顺序有关,假如当前定位在账号输入框,按键
盘的 tab键后遍历的不是密码框,那就不法输入密码。假如输入密码后,还有
需要填写验证码,那么回车也起不到登陆的效果。
9.2 、键盘组合键用法
#coding=utf-8
fromselenium import webdriver
fromselenium.webdriver.common.keys import Keys
import time
driver = webdriver.Firefox()
driver.get(“http://www.baidu.com”)
博客园 — 虫师
http://fnng.cnblogs.com 25
#输入框输入内容
driver.find_element_by_id(“kw”).send_keys(“selenium”)
time.sleep(3)
#ctrl+a 全选输入框内容
driver.find_element_by_id(“kw”).send_keys(Keys.CONTROL,’a’)
time.sleep(3)
#ctrl+x 剪切输入框内容
driver.find_element_by_id(“kw”).send_keys(Keys.CONTROL,’x’)
time.sleep(3)
#输入框重新输入内容,搜索
driver.find_element_by_id(“kw”).send_keys(u”虫师 cnblogs”)
driver.find_element_by_id(“su”).click()
time.sleep(3)
driver.quit()
上面的操作没有实际意义,但向我们演示了键盘组合按键的用法。
9.3 、中文乱码问题
selenium2 python 在 send_keys()中输入中文一直报错,其实前面加个小 u 就解决了:
coding=utf-8
send_keys(u”输入中文”)
需要注意的是 utf-8并不是万能的,我们需要保持脚本、浏览器、程序三者编码之
间的转换;如果 utf-8不能解决,可以尝试 GBK或修改浏览器的默认编码。
十、鼠标事件
本 章 重点:
ActionChains类
 context_click() 右击
 double_click() 双击
 drag_and_drop() 拖动
测试的产品中有一个操作是右键点击文件列表会弹出一个快捷菜单,可以方
便的选择快捷菜单中的选择对文件进行操作(删除、移动、重命名),之前学习
元素的点击非常简单:
driver.find_element_by_id(“xxx”).click()
博客园 — 虫师
http://fnng.cnblogs.com 26
那么鼠标的双击、右击、拖动等是否也是这样的写法呢?例如右击:
driver.find_element_by_id(“xxx”).context_click()
经过运行脚本得到了下面的错误提示:
AttributeError:’WebElement’objecthasnoattribute’context_click’
提示右点方法不属于 webelement 对象,通过查找文档,发现属于
ActionChains类,但文档中没有具体写法。这里要感谢 北京-QC-rabbit 的指
点,其实整个python+selenium学习过程都要感谢北京-QC-rabbit的指点。
10.1 、鼠标右键
下面介绍鼠标右键的用法,以快播私有云为例:
#coding=utf-8
fromselenium import webdriver
fromselenium.webdriver.common.action_chainsimport ActionChains
import time
driver = webdriver.Firefox()
driver.get(“http://passport.kuaibo.com/login/?referrer=http%3A%2F%2Fwebcloud
.kuaibo.com%2F”)
#登陆快播私有云
driver.find_element_by_id(“user_name”).send_keys(“username”)
driver.find_element_by_id(“user_pwd”).send_keys(“123456”)
driver.find_element_by_id(“dl_an_submit”).click()
time.sleep(3)
#定位到要右击的元素
qqq
=driver.find_element_by_xpath(“/html/body/div/div[2]/div[2]/div/div[3]/table
/tbody/tr/td[2]”)
#对定位到的元素执行鼠标右键操作
ActionChains(driver).context_click(qqq).perform()
博客园 — 虫师
http://fnng.cnblogs.com 27
”’
#你也可以使用三行的写法,但我觉得上面两行写法更容易理解
chain = ActionChains(driver)
implement=
driver.find_element_by_xpath(“/html/body/div/div[2]/div[2]/div/div[3]/table/
tbody/tr/td[2]”)
chain.context_click(implement).perform()
”’
time.sleep(3) #休眠3秒
driver.close()
这里需要注意的是,在使用 ActionChains 类之前,要先将包引入。
右击的操作会了,下面的其它方法比葫芦画瓢也能写出来。
10.2 、鼠标双击
鼠标双击的写法:
#定位到要双击的元素
qqq =driver.find_element_by_xpath(“xxx”)
#对定位到的元素执行鼠标双击操作
ActionChains(driver).double_click(qqq).perform()
10.3 、鼠标拖放
鼠标拖放操作的写法:
#定位元素的原位置
element =driver.find_element_by_name(“source”)
#定位元素要移动到的目标位置
target = driver.find_element_by_name(“target”)
博客园 — 虫师
http://fnng.cnblogs.com 28
#执行元素的移动操作
ActionChains(driver).drag_and_drop(element, target).perform()
十一、定位一组元素
webdriver 可以很方便的使用 findElement 方法来定位某个特定的对象,不过有时
候我们却需要定位一组对象,这时候就需要使用 findElements 方法。
定位一组对象一般用于以下场景:
 批量操作对象,比如将页面上所有的 checkbox 都勾上
 先获取一组对象,再在这组对象中过滤出需要具体定位的一些对象。比如定位出页
面上所有的 checkbox,然后选择最后一个
checkbox.html
<html>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″ />
<title>Checkbox</title>
<script type=”text/javascript” async=””
src=”https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js”></script>
<link
href=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined
.min.css” rel=”stylesheet”/>
<script
src=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js”></
script>
</head>
<body>
<h3>checkbox</h3>
<div class=”well”>
<formclass=”form-horizontal”>
<divclass=”control-group”>
<labelclass=”control-label” for=”c1″>checkbox1</label>
<div class=”controls”>
<input type=”checkbox” id=”c1″ />
</div>
</div>
<divclass=”control-group”>
博客园 — 虫师
http://fnng.cnblogs.com 29
<labelclass=”control-label” for=”c2″>checkbox2</label>
<div class=”controls”>
<input type=”checkbox” id=”c2″ />
</div>
</div>
<divclass=”control-group”>
<labelclass=”control-label” for=”c3″>checkbox3</label>
<div class=”controls”>
<input type=”checkbox” id=”c3″ />
</div>
</div>
<divclass=”control-group”>
<labelclass=”control-label” for=”r”>radio</label>
<div class=”controls”>
<input type=”radio” id=”r1″ />
</div>
</div>
<divclass=”control-group”>
<labelclass=”control-label” for=”r”>radio</label>
<div class=”controls”>
<input type=”radio” id=”r2″ />
</div>
</div>
</form>
</div>
</body>
</html>
将这段代码保存复制到记事本中,将保存成 checkbox.html文件。(注意,这个页面需要
和我们的自动化脚本放在同一个目录下)
通过浏览器打开,得到下列页面:
博客园 — 虫师
http://fnng.cnblogs.com 30
11.1 、第一种定位方法
通过浏览器打个这个页面我们看到三个复选框和两个单选框。下面我们就来定位这
三个复选框。
# -*- coding: utf-8-*-
fromselenium import webdriver
import time
import os
dr =webdriver.Firefox()
file_path= ‘file:///’ + os.path.abspath(‘checkbox.html’)
dr.get(file_path)
# 选择页面上所有的 input,然后从中过滤出所有的 checkbox 并勾选之
inputs = dr.find_elements_by_tag_name(‘input’)
for inputin inputs:
if input.get_attribute(‘type’) == ‘checkbox’:
input.click()
time.sleep(2)
dr.quit()
import os
博客园 — 虫师
http://fnng.cnblogs.com 31
注意:因为我们调用的是本地文件, 所以要导入 os包。
11.2 、第二种定位方法
第二种写法与第一种写法差别不大,都是通过一个循环来勾选控件。
# -*- coding: utf-8-*-
fromselenium import webdriver
import time
import os
dr =webdriver.Firefox()
file_path= ‘file:///’ + os.path.abspath(‘checkbox.html’)
dr.get(file_path)
# 选择所有的 checkbox 并全部勾上
checkboxes = dr.find_elements_by_css_selector(‘input[type=checkbox]’)
for checkbox in checkboxes:
checkbox.click()
time.sleep(2)
# 打印当前页面上有多少个 checkbox
print len(dr.find_elements_by_css_selector(‘input[type=checkbox]’))
time.sleep(2)
dr.quit()
11.3 、去掉最后一个勾选
还有一个问题,有时候我们并不想勾选页面的所有的复选框(checkbox),可以通
过下面办法把最后一个被勾选的框去掉。如下:
# -*- coding: utf-8-*-
from selenium import webdriver
import time
import os
dr = webdriver.Firefox()
博客园 — 虫师
http://fnng.cnblogs.com 32
file_path= ‘file:///’ + os.path.abspath(‘checkbox.html’)
dr.get(file_path)
# 选择所有的 checkbox 并全部勾上
checkboxes =
dr.find_elements_by_css_selector(‘input[type=checkbox]’)
for checkbox in checkboxes:
checkbox.click()
time.sleep(2)
# 把页面上最后1个 checkbox的勾给去掉
dr.find_elements_by_css_selector(‘input[type=checkbox]’).pop().cl
ick()
time.sleep(2)
dr.quit()
其实,去掉勾选表也逻辑也非常简单,就是再次点击勾选的按钮。可能我们比较迷
惑的是如何找到“最后一个”按钮。pop() 可以实现这个功能。
十二、多层框架/ 窗口定位
本节知识点:
多层框架或窗口的定位:
 switch_to_frame()
 switch_to_window()
对于一个现代的 web应用,经常会出现框架(frame) 或窗口(window)的应用,
这也就给我们的定位带来了一个难题。
有时候我们定位一个元素,定位器没有问题,但一直定位不了,这时候就要检查这
个元素是否在一个 frame 中,seelnium webdriver 提供了一个 switch_to_frame方
法,可以很轻松的来解决这个问题。
12.1 、多层框架定位
frame.html
<html>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″ />
<title>frame</title>
博客园 — 虫师
http://fnng.cnblogs.com 33
<script type=”text/javascript”
async=””src=”http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
“></script>
<link
href=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstra
p-combined.min.css” rel=”stylesheet” />
<script type=”text/javascript”>$(document).ready(function(){
});
</script>
</head>
<body>
<div class=”row-fluid”>
<div class=”span10 well”>
<h3>frame</h3>
<iframe id=”f1″ src=”inner.html” width=”800″,
height=”600″></iframe>
</div>
</div>
</body>
<script
src=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.
min.js”></script>
</html>
inner.html
<html>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″ />
<title>inner</title>
</head>
<body>
<div class=”row-fluid”>
<div class=”span6 well”>
<h3>inner</h3>
<iframe id=”f2″ src=”http://www.baidu.com”
width=”700″height=”500″></iframe>
<a href=”javascript:alert(‘watir-webdriver betterthan
selenium webdriver;’)”>click</a>
</div>
</div>
</body>
</html>
frame.html 中嵌套 inner.html ,两个文件和我们的脚本文件放同一个目录下,通过
博客园 — 虫师
http://fnng.cnblogs.com 34
浏览器打开,得到下列页面:
下面通过 switch_to_frame() 方法来进行定位:
#coding=utf-8
fromselenium import webdriver
import time
import os
browser =webdriver.Firefox()
file_path= ‘file:///’ + os.path.abspath(‘frame.html’)
browser.get(file_path)
browser.implicitly_wait(30)
#先找到到 ifrome1(id = f1)
browser.switch_to_frame(“f1”)
#再找到其下面的 ifrome2(id =f2)
browser.switch_to_frame(“f2”)
#下面就可以正常的操作元素了
博客园 — 虫师
http://fnng.cnblogs.com 35
browser.find_element_by_id(“kw”).send_keys(“selenium”)
browser.find_element_by_id(“su”).click()
time.sleep(3)
browser.quit()
12.2 、多层窗口定位
有可能嵌套的不是框架,而是窗口,还有真对窗口的方法:switch_to_window
用法与 switch_to_frame 相同:
driver.switch_to_window(“windowName”)
十三、层级定位
假如两个控件,他们长的一模样,还都叫“张三”,唯一的不同是一个在北京,一
个在上海,那我们就可以通过,他们的城市,区,街道,来找到他们。
在实际的测试中也经常会遇到这种问题:页面上有很多个属性基本相同的元素,现
在需要具体定位到其中的一个。由于属性基本相当,所以在定位的时候会有些麻烦,这
时候就需要用到层级定位。先定位父元素,然后再通过父元素定位子孙元素。
level_locate.html
<html>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″ />
<title>Level Locate</title>
<script type=”text/javascript” async=””
src=”http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js”></scri
pt>
<link
href=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-c
ombined.min.css” rel=”stylesheet” />
</head>
<body>
博客园 — 虫师
http://fnng.cnblogs.com 36
<h3>Levellocate</h3>
<divclass=”span3″>
<divclass=”well”>
<divclass=”dropdown”>
<a class=”dropdown-toggle” data-toggle=”dropdown”
href=”#”>Link1</a>
<ul class=”dropdown-menu” role=”menu”
aria-labelledby=”dLabel”id=”dropdown1″>
<li><a tabindex=”-1″ href=”#”>Action</a></li>
<li><a tabindex=”-1″ href=”#”>Another action</a></li>
<li><a tabindex=”-1″ href=”#”>Something elsehere</a></li>
<li class=”divider”></li>
<li><a tabindex=”-1″ href=”#”>Separatedlink</a></li>
</ul>
</div>
</div>
</div>
<divclass=”span3″>
<divclass=”well”>
<divclass=”dropdown”>
<a class=”dropdown-toggle” data-toggle=”dropdown”
href=”#”>Link2</a>
<ul class=”dropdown-menu” role=”menu”
aria-labelledby=”dLabel”>
<li><a tabindex=”-1″ href=”#”>Action</a></li>
<li><a tabindex=”-1″ href=”#”>Another action</a></li>
<li><a tabindex=”-1″ href=”#”>Something elsehere</a></li>
<li class=”divider”></li>
<li><a tabindex=”-1″ href=”#”>Separatedlink</a></li>
</ul>
</div>
</div>
</div>
</body>
<script
src=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min
.js”></script>
</html>
上面的 html 代码比较乱,请复制到编辑器中查看,如 nodepad ++ 编辑器。
(注意,这个页面需要和我们的自动化脚本放在同一个目录下)通过浏览器打开:
博客园 — 虫师
http://fnng.cnblogs.com 37
定位思路:
具体思路是:先点击显示出1个下拉菜单,然后再定位到该下拉菜单所在的 ul,再
定位这个 ul 下的某个具体的 link。在这里,我们定位第1个下拉菜单中的 Action 这个
选项。
脚本如下:
# -*- coding: utf-8-*-
fromselenium import webdriver
fromselenium.webdriver.support.uiimport WebDriverWait
import time
import os
dr =webdriver.Firefox()
file_path= ‘file:///’ + os.path.abspath(‘level_locate.html’)
dr.get(file_path)
#点击 Link1链接(弹出下拉列表)
dr.find_element_by_link_text(‘Link1’).click()
#找到 id 为 dropdown1的父元素
WebDriverWait(dr, 10).until(lambda the_driver:
the_driver.find_element_by_id(‘dropdown1’).is_displayed())
#在父亲元件下找到 link 为 Action 的子元素
menu = dr.find_element_by_id(‘dropdown1’).find_element_by_link_text(‘Action’)
#鼠标定位到子元素上
webdriver.ActionChains(dr).move_to_element(menu).perform()
time.sleep(2)
博客园 — 虫师
http://fnng.cnblogs.com 38
dr.quit()
WebDriverWait(dr, 10)
10秒内每隔500毫秒扫描1次页面变化,当出现指定的元素后结束。dr 就不解释了,前
面操作 webdriver.firefox()的句柄
is_displayed()
该元素是否用户可以见
class ActionChains(driver)
driver: 执行用户操作实例 webdriver
生成用户的行为。所有的行动都存储在 actionchains 对象。通过 perform()存储的行为。
move_to_element(menu)
移动鼠标到一个元素中,menu 上面已经定义了他所指向的哪一个元素
to_element:元件移动到
perform()
执行所有存储的行为
十四、上传文件操作
文件上传操作也比较常见功能之一,上传功能没有用到新有方法或函数,关键是思
路。
上传过程一般要打开一个本地窗口,从窗口选择本地文件添加。所以,一般会卡在
如何操作本地窗口添加上传文件。
其实,在 selenium webdriver 没我们想的那么复杂;只要定位上传按钮,通
send_keys 添加本地文件路径就可以了。绝对路径和相对路径都可以,关键是上传的文
件存在。下面通地例子演示。
博客园 — 虫师
http://fnng.cnblogs.com 39
14.1 、操作文件上传例子
upload_file.html
<html>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″ />
<title>upload_file</title>
<script type=”text/javascript”
async=””src=”http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
“></script>
<link
href=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstra
p-combined.min.css” rel=”stylesheet” />
<script type=”text/javascript”>
</script>
</head>
<body>
<div class=”row-fluid”>
<div class=”span6 well”>
<h3>upload_file</h3>
<input type=”file” name=”file” />
</div>
</div>
</body>
<script
src=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.
min.js”></script>
</html>
通过浏览器打开,得到下列页面:
操作上传脚本:
#coding=utf-8
from selenium import webdriver
博客园 — 虫师
http://fnng.cnblogs.com 40
import os,time
driver = webdriver.Firefox()
#脚本要与 upload_file.html 同一目录
file_path= ‘file:///’ + os.path.abspath(‘upload_file.html’)
driver.get(file_path)
#定位上传按钮,添加本地文件
driver.find_element_by_name(“file”).send_keys(‘D:\\selenium_use_c
ase\upload_file.txt’)
time.sleep(2)
driver.quit()
14.2 、139  邮箱上传
其它有些应用不好找,所以就自己创建页面,这样虽然麻烦,但脚本代码突出重点。
这里找一139邮箱的实例,有帐号的同学可以测试一下~!
(登陆基础版的139邮箱,网盘模块上传文件)
#coding=utf-8
fromselenium import webdriver
import os,time
driver = webdriver.Firefox()
driver.get(“http://m.mail.10086.cn”)
driver.implicitly_wait(30)
#登陆
driver.find_element_by_id(“ur”).send_keys(“手机号”)
driver.find_element_by_id(“pw”).send_keys(“密码”)
driver.find_element_by_class_name(“loading_btn”).click()
time.sleep(3)
#进入139网盘模块
driver.find_element_by_xpath(“/html/body/div[3]/a[9]/span[2]”).click()
博客园 — 虫师
http://fnng.cnblogs.com 41
time.sleep(3)
#上传文件
driver.find_element_by_id(“id_file”).send_keys(‘D:\\selenium_use_case\upload
_file.txt’)
time.sleep(5)
driver.quit()
十五、下拉框处理
本节重点
 处理下拉框
 switch_to_alert()
 accept()
下拉框是我们最常见的一种页面元素,对于一般的元素,我们只需要一次就定位,但下
拉框里的内容需要进行两次定位,先定位到下拉框,再定位到下拉框内里的选项。
15.1 、操作下拉框例子
drop_down.html
<html>
<body>
<select id=”ShippingMethod”
onchange=”updateShipping(options[selectedIndex]);” name=”ShippingMethod”>
<option value=”12.51″>UPS Next Day Air ==> $12.51</option>
<option value=”11.61″>UPS Next Day Air Saver ==> $11.61</option>
<option value=”10.69″>UPS 3 Day Select ==> $10.69</option>
<option value=”9.03″>UPS 2nd Day Air ==> $9.03</option>
<option value=”8.34″>UPS Ground ==> $8.34</option>
<option value=”9.25″>USPS Priority Mail Insured ==> $9.25</option>
<option value=”7.45″>USPS Priority Mail ==> $7.45</option>
<option value=”3.20″ selected=””>USPS First Class ==> $3.20</option>
博客园 — 虫师
http://fnng.cnblogs.com 42
</select>
</body>
</html>
保存并通过浏览器打开,如下:
现在我们来通过脚本选择下拉列表里的 $10.69
#-*-coding=utf-8
fromselenium import webdriver
import os,time
driver= webdriver.Firefox()
file_path= ‘file:///’ + os.path.abspath(‘drop_down.html’)
driver.get(file_path)
time.sleep(2)
#先定位到下拉框
m=driver.find_element_by_id(“ShippingMethod”)
#再点击下拉框下的选项
m.find_element_by_xpath(“//option[@value=’10.69′]”).click()
time.sleep(3)
driver.quit()
解析:
这里可能和之前的操作有所不同,首先要定位到下拉框的元素,然后选择下拉列表
中的选项进行点击操作。
m=driver.find_element_by_id(“ShippingMethod”)
m.find_element_by_xpath(“//option[@value=’10.69′]”).click()
博客园 — 虫师
http://fnng.cnblogs.com 43
15.2 、百度搜索设置下拉框操作
#-*-coding=utf-8
fromselenium import webdriver
import os,time
driver= webdriver.Firefox()
driver.get(“http://www.baidu.com”)
#进入搜索设置页
driver.find_element_by_link_text(“搜索设置”).click()
#设置每页搜索结果为100条
m=driver.find_element_by_name(“NR”)
m.find_element_by_xpath(“//option[@value=’100′]”).click()
time.sleep(2)
#保存设置的信息
driver.find_element_by_xpath(“//input[@value=’保存设置’]”).click()
time.sleep(2)
driver.switch_to_alert().accept()
#跳转到百度首页后,进行搜索表(一页应该显示100条结果)
driver.find_element_by_id(“kw”).send_keys(“selenium”)
driver.find_element_by_id(“su”).click()
time.sleep(3)
driver.quit()
解析:
当我们在保存百度的设置时会会弹出一个确定按钮;我们并没按照常规的方法去定
位弹窗上的“确定”按钮,而是使用:
driver.switch_to_alert().accept()
完成了操作,这是因为弹窗比较是一个具有唯一性的警告信息,所以可以用这种简便
的方法处理。
– switch_to_alert()
博客园 — 虫师
http://fnng.cnblogs.com 44
焦点集中到页面上的一个警告(提示)
– accept()
接受警告提示
十六、alert 、confirm 、prompt  的处理
本节重点:
 text 返回 alert/confirm/prompt 中的文字信息
 accept 点击确认按钮
 dismiss 点击取消按钮,如果有的话
 send_keys 输入值,这个 alert\confirm 没有对话框就不能用了,不然会报错。
在实际的应用中,我们会碰到各种交互的弹窗,在上面百度搜索设置的例子中,我们用
switch_to_alert() 处理警告框非常简单;其实,对于原生的 js alert 、confirm 以及 prompt
都可以通过 webdriver 的 switch_to_alert()方法进行处理。
比较常见下的就是下面这种类型的确认框:
selenium.webdriver.remote.webdriver.switch_to_alert()
将焦点切换到页面上的警报
博客园 — 虫师
http://fnng.cnblogs.com 45
Usage:
driver.switch_to_alert()
由于用法简单,这里就不给具体例子了,在实际应用中:
#接受警告信息
alert = driver.switch_to_alert()
alert.accept()
#得到文本信息打印
alert = driver.switch_to_alert()
print alert.text()
#取消对话框(如果有的话)
alert = driver.switch_to_alert()
alert.dismiss()
#输入值
alert = driver.switch_to_alert()
alert.send_keys(“xxx”)
十七、 对话框的处理
本节重点:
 打开对话框
 关闭对话框
 操作对话框中的元素
 current_window_handle 获得当前窗口
 window_handles 获得所有窗口
更多的时候我们在实际的应用中碰到的并不是简单警告框,而是提供更多功能的会话
框。
17.1 、div  对话框的处理
modal.html
博客园 — 虫师
http://fnng.cnblogs.com 46
<html>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″ />
<title>modal</title>
<script type=”text/javascript” async=””
src=”http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js”></script>
<link
href=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css”
rel=”stylesheet” />
<script type=”text/javascript”>
$(document).ready(function(){
$(‘#click’).click(function(){
$(this).parent().find(‘p’).text(‘Click on the link to success!’);
});
});
</script>
</head>
<body>
<h3>modal</h3>
<div class=”row-fluid”>
<div class=”span6″>
<!– Button to trigger modal –>
<a href=”#myModal” role=”button” class=”btn btn-primary”
data-toggle=”modal” id=”show_modal”>Click</a>
<!– Modal –>
<div id=”myModal” class=”modal hide fade” tabindex=”-1″
role=”dialog” aria-labelledby=”myModalLabel” aria-hidden=”true”>
<div class=”modal-header”>
<button type=”button” class=”close” data-dismiss=”modal”
aria-hidden=”true”>×</button>
<h3 id=”myModalLabel”>Modal header</h3>
</div>
<div class=”modal-body”>
<p>Congratulations, you open the window!</p>
<a href=”#” id=”click”>click me</a>
</div>
<div class=”modal-footer”>
<button class=”btn” data-dismiss=”modal”
aria-hidden=”true”>Close</button>
<button class=”btn btn-primary”>Save changes</button>
</div>
</div>
博客园 — 虫师
http://fnng.cnblogs.com 47
<html>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″ />
<title>modal</title>
<script type=”text/javascript” async=””
src=”http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js”></script>
<link
href=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css”
rel=”stylesheet” />
<script type=”text/javascript”>
$(document).ready(function(){
$(‘#click’).click(function(){
$(this).parent().find(‘p’).text(‘Click on the link to success!’);
});
});
</script>
</head>
<body>
<h3>modal</h3>
<div class=”row-fluid”>
<div class=”span6″>
<!– Button to trigger modal –>
<a href=”#myModal” role=”button” class=”btn btn-primary”
data-toggle=”modal” id=”show_modal”>Click</a>
<!– Modal –>
<div id=”myModal” class=”modal hide fade” tabindex=”-1″
role=”dialog” aria-labelledby=”myModalLabel” aria-hidden=”true”>
<div class=”modal-header”>
<button type=”button” class=”close” data-dismiss=”modal”
aria-hidden=”true”>×</button>
<h3 id=”myModalLabel”>Modal header</h3>
</div>
<div class=”modal-body”>
<p>Congratulations, you open the window!</p>
<a href=”#” id=”click”>click me</a>
</div>
<div class=”modal-footer”>
<button class=”btn” data-dismiss=”modal”
aria-hidden=”true”>Close</button>
<button class=”btn btn-primary”>Save changes</button>
</div>
</div>
博客园 — 虫师
http://fnng.cnblogs.com 48
</div>
</div>
</body>
<script
src=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js”></script>
</html>
代码有点长,你可以直接赋值粘贴到 Notepad++中,保存成 html 通过浏览器打开,效
果如下:
操作脚本如下:
# -*- coding: utf-8-*-
fromselenium import webdriver
fromtimeimport sleep
import os
import selenium.webdriver.support.ui asui
if ‘HTTP_PROXY’in os.environ:del os.environ[‘HTTP_PROXY’]
dr =webdriver.Firefox()
file_path= ‘file:///’ +os.path.abspath(‘modal.html’)
dr.get(file_path)
# 打开对话框
dr.find_element_by_id(‘show_modal’).click()
sleep(3)
# 点击对话框中的链接
博客园 — 虫师
http://fnng.cnblogs.com 49
# 由于对话框中的元素被蒙板所遮挡,直接点击会报 Element is not clickable 的错误
# 所以使用 js 来模拟 click
link= dr.find_element_by_id(‘myModal’).find_element_by_id(‘click’)
dr.execute_script(‘$(arguments[0]).click()’,link)
sleep(4)
# 关闭对话框
buttons =
dr.find_element_by_class_name(‘modal-footer’).find_elements_by_tag_name(‘but
ton’)
buttons[0].click()
dr.quit()
17.2 、一般对话框的处理
有些弹出对话框窗,我们可以通过判断是否为当前窗口的方式进行操作。
#获得当前窗口
nowhandle=driver.current_window_handle
#打开弹窗
driver.find_element_by_name(“xxx”).click()
#获得所有窗口
allhandles=driver.window_handles
for handle in allhandles:
if handle!=nowhandle: #比较当前窗口是不是原先的窗口
driver.switch_to_window(handle) #获得当前窗口的句柄
dirver.find_element_by_class_name(“xxxx”).click() #在当前窗口操作
博客园 — 虫师
http://fnng.cnblogs.com 50
#回到原先的窗口
driver.switch_to_window(nowhandle)
这里只是操作窗口的代码片段,提供一个思路,能否完成我们想要的结果,还需要我们
通过实例去验证。
十八、调用 js
本节重点:
调用 js 方法
 execute_script(script, *args)
在当前窗口/框架 同步执行 javaScript
script:JavaScript 的执行。
*args:适用任何 JavaScript 脚本。
使用:
driver.execute_script(‘document.title’)
18.1 、通过 js  隐藏元素
js.html
<html>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=utf-8″ />
<title>js</title>
<script type=”text/javascript” async=””
src=”http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js”></s
cript>
<link
博客园 — 虫师
http://fnng.cnblogs.com 51
href=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstra
p-combined.min.css” rel=”stylesheet” />
<script type=”text/javascript”> $(document).ready(function(){
$(‘#tooltip’).tooltip({“placement”: “right”});
});
</script>
</head>
<body>
<h3>js</h3>
<div class=”row-fluid”>
<div class=”span6 well”>
<a id=”tooltip” href=”#” data-toggle=”tooltip” title=”
selenium-webdriver(python)”>hover to see tooltip</a>
<a class=”btn”>Button</a>
</div>
</div>
</body>
<script
src=”http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.
min.js”></script>
</html>
(保持 html 文件与执行脚本在同一目录下)
保存并通过浏览器打开,如下:
执行 js 一般有两种场景:
 一种是在页面上直接执行 JS
 另一种是在某个已经定位的元素上执行 JS
#coding=utf-8
fromselenium import webdriver
import time,os
博客园 — 虫师
http://fnng.cnblogs.com 52
driver = webdriver.Firefox()
file_path= ‘file:///’ + os.path.abspath(‘js.html’)
driver.get(file_path)
#######通过 JS 隐藏选中的元素##########第一种方法:
driver.execute_script(‘$(“#tooltip”).fadeOut();’)
time.sleep(5)
#第二种方法:
button = driver.find_element_by_class_name(‘btn’)
driver.execute_script(‘$(arguments[0]).fadeOut()’,button)
time.sleep(5)
driver.quit()
18.2 、通过 js  使输入框标红
#coding=utf-8
fromselenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(“http://passport.kuaibo.com/login/?referrer=http%3A%2F%2Fvod.kuai
bo.com%2F%3Ft%3Dhome”)
#给用户名的输入框标红
js=”var q=document.getElementById(\”user_name\”);q.style.border=\”1px solid
red\”;”
#调用 js
driver.execute_script(js)
time.sleep(3)
driver.find_element_by_id(“user_name”).send_keys(“username”)
driver.find_element_by_id(“user_pwd”).send_keys(“password”)
driver.find_element_by_id(“dl_an_submit”).click()
博客园 — 虫师
http://fnng.cnblogs.com 53
time.sleep(3)
driver.quit()
js  解释:
q=document.getElementById(\”user_name\”)
元素 q的 id 为 user_name
q.style.border=\”1px solid red\
元素 q的样式,边框为1个像素红色
十九、控制浏览器滚动条
有时候我们需要控制页面滚动条上的滚动条,但滚动条并非页面上的元素,这个时
候就需要借助 js 是来进行操作。一般用到操作滚动条的会两个场景:
 注册时的法律条文需要阅读,判断用户是否阅读的标准是:滚动条是否拉到最
下方。
 要操作的页面元素不在吸视范围,无法进行操作,需要拖动滚动条
其实,实现这个功能只要一行代码,但由于不懂 js ,所以花了不小力气找到这种方法。
用于标识滚动条位置的代码
<body onload= “document.body.scrollTop=0”>
<body onload= “document.body.scrollTop=100000”>
如果滚动条在最上方的话,scrollTop=0,那么要想使用滚动条在最可下方,可以
scrollTop=100000 ,这样就可以使滚动条在最下方。
博客园 — 虫师
http://fnng.cnblogs.com 54
19.1 、场景一
先来解决场第一个问题,法律条款是一个内嵌窗口,通过 firebug 工具可以定位到内嵌
入窗口可以定位到元素的 id ,可以通过下面的代码实现。
js=”var q=document.getElementById(‘id’).scrollTop=10000″
driver.execute_script(js)
19.2 、场景二
有滚动条的页面到处可见,这个就比较容易找例子,我们以操作百度搜索结果页为例:
#coding=utf-8
fromselenium import webdriver
import time
#访问百度
driver=webdriver.Firefox()
driver.get(“http://www.baidu.com”)
#搜索
driver.find_element_by_id(“kw”).send_keys(“selenium”)
driver.find_element_by_id(“su”).click()
time.sleep(3)
#将页面滚动条拖到底部
js=”var q=document.documentElement.scrollTop=10000″driver.execute_script(js)
time.sleep(3)
#将滚动条移动到页面的顶部
js=”var q=document.documentElement.scrollTop=0″driver.execute_script(js)
time.sleep(3)
driver.quit()
博客园 — 虫师
http://fnng.cnblogs.com 55
二十、cookie  处理
本节重点:
 driver.get_cookies() 获得 cookie 信息
 add_cookie(cookie_dict) 向 cookie 添加会话信息
 delete_cookie(name) 删除特定(部分)的 cookie
 delete_all_cookies() 删除所有 cookie
通过 webdriver 操作 cookie是一件非常有意思的事儿,有时候我们需要了解浏览器中
是否存在了某个 cookie 信息,webdriver 可以帮助我们读取、添加,删除 cookie 信息。
20.1 、打印 cookie  信息
#coding=utf-8
fromselenium import webdriver
import time
driver = webdriver.Chrome()
driver.get(“http://www.youdao.com”)
# 获得 cookie 信息
cookie= driver.get_cookies()
#将获得 cookie 的信息打印
print cookie
driver.quit()
运行打印信息:
[{u’domain’: u’.youdao.com’, u’secure’: False, u’value’:
u’aGFzbG9nZ2VkPXRydWU=’, u’expiry’: 1408430390.991375, u’path’: u’/’,
u’name’: u’_PREF_ANONYUSER__MYTH’}, {u’domain’: u’.youdao.com’, u’secure’:
False, u’value’: u’1777851312@218.17.158.115′, u’expiry’: 2322974390.991376,
u’path’: u’/’, u’name’: u’OUTFOX_SEARCH_USER_ID’}, {u’path’: u’/’, u’domain’:
u’www.youdao.com’, u’name’: u’JSESSIONID’, u’value’:
博客园 — 虫师
http://fnng.cnblogs.com 56
u’abcUX9zdw0minadIhtvcu’, u’secure’: False}]
20.2 、对 cookie  操作
上面的方式打印了所有 cookie 信息,太多太乱,我们只想有真对性的打印自己想要
的信息,看下面的例子
#coding=utf-8
fromselenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(“http://www.youdao.com”)
#向 cookie 的 name 和 value 添加会话信息。
driver.add_cookie({‘name’:’key-aaaaaaa’, ‘value’:’value-bbbb’})
#遍历 cookies 中的 name 和 value 信息打印,当然还有上面添加的信息
for cookie in driver.get_cookies():
print “%s-> %s” % (cookie[‘name’], cookie[‘value’])
# 下面可以通过两种方式删除 cookie# 删除一个特定的 cookie
driver.delete_cookie(“CookieName”)
# 删除所有 cookie
driver.delete_all_cookies()
time.sleep(2)
driver.close()
运行打印信息:
YOUDAO_MOBILE_ACCESS_TYPE -> 1
_PREF_ANONYUSER__MYTH ->aGFzbG9nZ2VkPXRydWU=
OUTFOX_SEARCH_USER_ID ->-1046383847@218.17.158.115
JSESSIONID -> abc7qSE_SBGsVgnVLBvcu
key-aaaaaaa ->value-bbbb # 这一条是我们自己添加的
博客园 — 虫师
http://fnng.cnblogs.com 57
20.3 、博客园登陆分析cookie
通过博客园登陆来分析 cookie
#coding=utf-8
fromselenium import webdriver
import time
driver = webdriver.Firefox()
driver.get(“http://passport.cnblogs.com/login.aspx?ReturnUrl=http://www.cnbl
ogs.com/fnng/admin/EditPosts.aspx”)
time.sleep(3)
driver.maximize_window()# 浏览器全屏显示
#通过用户名密码登陆
driver.find_element_by_id(“tbUserName”).send_keys(“fnngj”)
driver.find_element_by_id(“tbPassword”).send_keys(“123456”)
#勾选保存密码
driver.find_element_by_id(“chkRemember”).click()
time.sleep(3)
#点击登陆按钮
driver.find_element_by_id(“btnLogin”).click()
#获取 cookie 信息并打印
cookie= driver.get_cookies()
print cookie
time.sleep(2)
driver.close()
运行打印信息:
#第一次执行信息
>>>
[{u’domain’: u’.cnblogs.com’, u’name’: u’.DottextCookie’, u’value’:
u’C709F15A8BC0B3E8D9AD1F68B371053849F7FEE31F73F1292A150932FF09A7B0D4A1B449A3
2A6B24AD986CDB05B9998471A37F39C3B637E85E481AA986D3F8C187D7708028F9D4ED3B326B
46DC43B416C47B84D706099ED1D78B6A0FC72DCF948DB9D5CBF99D7848FDB78324′,
博客园 — 虫师
http://fnng.cnblogs.com 58
u’expiry’: None, u’path’: u’/’, u’secure’: False}]
>>> =========================RESTART ================================
#第二次执行信息
>>>
[{u’domain’: u’.cnblogs.com’, u’name’: u’.DottextCookie’, u’value’:
u’5BB735CAD62E99F8CCB9331C32724E2975A0150D199F4243AD19357B3F99A416A93B2E803F
4D5C9D065429713BE8B5DB4ED760EDCBAF492EABE2158B3A6FBBEA2B95C4DA3D2EFEADACC324
7040906F1462731F652199E2A8BEFD8A9B6AAE87CF3059A3CAEB9AB0D8B1B7AD2A’,
u’expiry’: 1379502502, u’path’: u’/’, u’secure’: False}]
>>>
第一次注释掉勾选保存密码的操作,第二次通过勾选保存密码获得 cookie 信息 ;
来看两次运行结果的 cookie 的何不同:
u’expiry’: None
u’expiry’: 1379502502
通过对比发现,不勾选保存密码时 expiry 的值为 none ; 那么就可以初步判断勾选
保存密码的操作在 cookie 中起到了作用。至于是否准确可以再做进一步的分析。
二十一、webdriver  原理解析
之前看乙醇视频中提到,selenium 的 ruby 实现有一个小后门,在代码前加上
$DEBUG=1 ,再运行脚本的过程中,就可以看到客户端请求的信息与服务器端返回的数据;
觉得这个功能很强大,可以帮助理解 webdriver 的运行原理。
后来查了半天,python 并没有提供这样一个方便的后门,不过我们可以通过代理的方式
获得这些交互信息;
一、需要安装 java 虚拟机与 selenium-server-standalone ,参考本文档第一章环境搭
建第7、8步操作。
二、通过下面命令启动服务:
C:\selenium>java -jar selenium-server-standalone-2.33.0.jar
在命令结尾加 >d:\log.txt 可以将命令信息存入文件,但信息很少。
运行下面的自动化脚本:
博客园 — 虫师
http://fnng.cnblogs.com 59
#coding =utf-8
import time
fromselenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
driver = webdriver.Remote(desired_capabilities=DesiredCapabilities.CHROME)
driver.get(“http://www.youdao.com”)
driver.find_element_by_name(“q”).send_keys(“hello”)
driver.find_element_by_name(“q”).send_keys(“key.ENTER”)
driver.close()
webdriver  原理:
1. WebDriver 启动目标浏览器,并绑定到指定端口。该启动的浏览器实例,做为 web driver
的 remote server。
2. Client 端通过 CommandExcuter 发送 HTTPRequest 给 remote server 的侦听端口(通信
协议: the webriver wire protocol)
3. Remote server 需要依赖原生的浏览器组件(如:IEDriver.dll,chromedriver.exe),来转
化转化浏览器的 native 调用。
查看命令提示符下的运行日志:
咋一看很乱,慢慢分析一下就发现很有意思!结合上面的脚本分析
————————————————————————启动代理进入监听状态
C:\selenium>java -jarselenium-server-standalone-2.33.0.jar
八月 22, 201310:19:48 上午org.openqa.grid.selenium.GridLaunchermain
INFO:Launchingastandalone server
10:19:48.734 INFO-Java:Oracle Corporation 23.21-b01
10:19:48.734 INFO-OS:WindowsXP 5.1 x86
10:19:48.734 INFO-v2.33.0, withCore v2.33.0.Builtfrom revision4e90c97
10:19:48.843INFO – RemoteWebDriver instances shouldconnectto:http://127.0.0.
1:4444/wd/hub
10:19:48.843 INFO-VersionJetty/5.1.x
10:19:48.843 INFO-StartedHttpContext[/selenium-server/driver,/selenium-server
/driver]
博客园 — 虫师
http://fnng.cnblogs.com 60
10:19:48.843 INFO-StartedHttpContext[/selenium-server,/selenium-server]
10:19:48.843 INFO-StartedHttpContext[/,/]
10:19:48.890 INFO-Startedorg.openqa.jetty.jetty.servlet.ServletHandler@176343e
10:19:48.890 INFO-StartedHttpContext[/wd,/wd]
10:19:48.906INFO – Started SocketListeneron 0.0.0.0:4444
10:19:48.906 INFO-Startedorg.openqa.jetty.jetty.Server@388c74
————————————————————————————–
创建新 session
10:20:38.593 INFO-Executing: [newsession: {platform=ANY,javascriptEnabled=tr
ue,browserName=chrome,version=}] atURL: /session)
10:20:38.593 INFO-Creating a new session for Capabilities[{platform=ANY,java
scriptEnabled=true,browserName=chrome,version=}]
webdrivr 通过 GET 方式发送请求
[0.921][INFO]: receivedWebriver request:GET/status
向 webdrver 返回响应,返回码200表示成功
[0.921][INFO]: sending Webriverresponse:200{
“sessionId”:””,
“status”: 0,
“value”:{
“build”: {
“version”:”alpha” },
“os”:{
“arch”: “x86”,
“name”: “WindowsNT”,
“version”:”5.1 SP3″ }
}
}
webdriver再次以 POST 方式发送请求,并启动浏览器相关信息
[0.984][INFO]: receivedWebriver request:POST /session{
“desiredCapabilities”:{
“browserName”: “chrome”,
“javascriptEnabled”: true,
“platform”:”ANY”,
“version”: “” }
博客园 — 虫师
http://fnng.cnblogs.com 61
}
[0.984][INFO]:Launchingchrome:”C:\ocuments and Settings\Administrator\LocalS
ettings\Application ata\Google\Chrome\Application\chrome.exe” –remote-debugging
-port=4223–no-first-run –enable-logging–logging-level=1–user-data-dir=”C:
\OCUME~1\AMINI~1\LOCALS~1\Temp\scoped_dir1808_7550″
–load-extension=”C:\OCUME~1\AMINI~1\LOCALS~1\Temp\scoped_dir1808_26821\internal”
–ignore-certificate-error
sdata:text/html;charset=utf-8,
[1.773][INFO]: sending Webriverresponse:303
webdriver 再次以GET 方法请求,这附加上了session 的信息
[1.778][INFO]: receivedWebriver request:GET/session/32b33aa585ccbbf7ba7853588
2852af3
服务器先对 sesssionID 进行解析,确认是 selenium调用的以及要访问的网址,
[1.779][INFO]: sending Webriverresponse:200{
“sessionId”:”32b33aa585ccbbf7ba78535882852af3″,
“status”: 0,
“value”:{
“acceptSslCerts”:true,
“applicationCacheEnabled”: false,
“browserConnectionEnabled”:false,
“browserName”: “chrome”,
“chrome”:{
“chromedriverVersion”:”2.0″ },
“cssSelectorsEnabled”: true,
“databaseEnabled”: true,
“handlesAlerts”: true,
“javascriptEnabled”: true,
“locationContextEnabled”:true,
“nativeEvents”:true,
“platform”:”Windows NT”,
“rotatable”: false,
“takesScreenshot”: true,
“version”: “27.0.1453.116”,
“webStorageEnabled”: true }
}
10:20:40.640 INFO-Done:/session
博客园 — 虫师
http://fnng.cnblogs.com 62
10:20:40.640 INFO-Executing: org.openqa.selenium.remote.server.handler.GetSess
ionCapabilities@14cf7a1at URL:/session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc)
10:20:40.640 INFO-Done:/session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc
10:20:40.656INFO – Executing:[get:http://www.youdao.com] atURL: /session/ac5
b2c71-5b1a-469e-814c-fdd09a2061fc/url)
webdriver 正试向服务器请求youdao 网站
[1.820][INFO]: receivedWebriver request:POST /session/32b33aa585ccbbf7ba785358
82852af3/url {
“url”:”http://www.youdao.com”}
[1.822][INFO]: waiting for pending navigations…
[1.829][INFO]: donewaitingforpendingnavigations
[2.073][INFO]: waiting for pending navigations…
[2.900][INFO]: donewaitingforpendingnavigations
获得服务器数据的应答
[2.900][INFO]: sending Webriverresponse:200{
“sessionId”:”32b33aa585ccbbf7ba78535882852af3″,
“status”: 0,
“value”:null}
10:20:41.734 INFO-Done:/session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/url
————————————————————–下面接着发送定位输入框的信息
10:20:41.734 INFO-Executing: [find element:By.name: q]at URL:/session/ac5b2
c71-5b1a-469e-814c-fdd09a2061fc/element)
[2.905][INFO]: receivedWebriver request:POST /session/32b33aa585ccbbf7ba785358
82852af3/element {
“using”:”name”,
“value”:”q”}
[2.905][INFO]: waiting for pending navigations…
[2.905][INFO]: donewaitingforpendingnavigations
[2.922][INFO]: waiting for pending navigations…
[2.922][INFO]: donewaitingforpendingnavigations
得到服务器应答
[2.922][INFO]: sending Webriverresponse:200{
“sessionId”:”32b33aa585ccbbf7ba78535882852af3″,
博客园 — 虫师
http://fnng.cnblogs.com 63
“status”: 0,
“value”:{
“ELEMENT”: “0.19427558477036655:1” }
}
10:20:41.765 INFO-Done:/session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element
10:20:41.765 INFO-Executing: [send keys: 0 org.openqa.selenium.support.events.
EventFiringWebDriver$EventFiringWebElement@a8215ba9, [h, e,l, l,o]]at URL:/s
ession/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element/0/value)
向定位到的输入框写入 hello
[2.936][INFO]: receivedWebriver request:POST /session/32b33aa585ccbbf7ba785358
82852af3/element/0.19427558477036655:1/value {
“id”: “0.19427558477036655:1”,
“value”:[“h”, “e”,”l”, “l”,”o”]
}
[2.936][INFO]: waiting for pending navigations…
[2.936][INFO]: donewaitingforpendingnavigations
[3.002][INFO]: waiting for pending navigations…
[3.002][INFO]: donewaitingforpendingnavigations
[3.002][INFO]: sending Webriverresponse:200{
“sessionId”:”32b33aa585ccbbf7ba78535882852af3″,
“status”: 0,
“value”:null}
10:20:41.843INFO – Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element/
0/value
再次发送定位输入框的请求
10:20:41.843 INFO-Executing: [find element:By.name: q]at URL:/session/ac5b2
c71-5b1a-469e-814c-fdd09a2061fc/element)
[3.006][INFO]: receivedWebriver request:POST /session/32b33aa585ccbbf7ba785358
82852af3/element {
“using”:”name”,
“value”:”q”}
[3.006][INFO]: waiting for pending navigations…
[3.006][INFO]: donewaitingforpendingnavigations
[3.016][INFO]: waiting for pending navigations…
博客园 — 虫师
http://fnng.cnblogs.com 64
[3.016][INFO]: donewaitingforpendingnavigations
[3.016][INFO]: sending Webriverresponse:200{
“sessionId”:”32b33aa585ccbbf7ba78535882852af3″,
“status”: 0,
“value”:{
“ELEMENT”: “0.19427558477036655:1” }
}
10:20:41.859 INFO-Done:/session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element
10:20:41.859 INFO-Executing: [send keys: 0 org.openqa.selenium.support.events.
EventFiringWebDriver$EventFiringWebElement@a8215ba9, [k, e,y, .,E, N,T, E,R]
]at URL:/session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element/0/value)
对定位的到的输入框发送回车(ENTER)事件请求
[3.021][INFO]: receivedWebriver request:POST /session/32b33aa585ccbbf7ba785358
82852af3/element/0.19427558477036655:1/value {
“id”: “0.19427558477036655:1”,
“value”:[“k”, “e”,”y”, “.”,”E”, “N”,”T”, “E”,”R”]
}
[3.021][INFO]: waiting for pending navigations…
[3.021][INFO]: donewaitingforpendingnavigations
[3.064][INFO]: waiting for pending navigations…
[3.064][INFO]: donewaitingforpendingnavigations
[3.064][INFO]: sending Webriverresponse:200{
“sessionId”:”32b33aa585ccbbf7ba78535882852af3″,
“status”: 0,
“value”:null}
10:20:41.906INFO – Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/element/
0/value
10:20:41.906 INFO – Executing: [close window] at URL:
/session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/window)
[3.068][INFO]: receivedWebriver request:ELETE/session/32b33aa585ccbbf7ba78535
882852af3/window
[WARNING:chrome_desktop_impl.cc(88)] chromedetaches,user shouldtake careof d
irectory:C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\scoped_dir1808_7550andC:\DOCUME~1\
ADMINI~1\LOCALS~1\Temp\scoped_dir1808_26821
[5.318][INFO]: sending Webriverresponse:200{
博客园 — 虫师
http://fnng.cnblogs.com 65
“sessionId”:”32b33aa585ccbbf7ba78535882852af3″,
“status”: 0,
“value”:null}
10:20:44.156INFO – Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/window
第二部分:框架的力量
博客园 — 虫师
http://fnng.cnblogs.com 66
二十二、引入 unittest  框架
unittest 框架学习
借助 IED 录制脚本,
博客园 — 虫师
http://fnng.cnblogs.com 67
将脚本导出,保存为 baidu.py ,通过 python IDLE 编辑器打开。如下:
fromselenium import webdriver
fromselenium.webdriver.common.by import By
fromselenium.webdriver.common.keys import Keys
fromselenium.webdriver.support.uiimport Select
fromselenium.common.exceptions import NoSuchElementException
import unittest, time, re
class Baidu(unittest.TestCase):
def setUp(self):
self.driver = webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url = “http://www.baidu.com/” self.verificationErrors =
[]
self.accept_next_alert =True
def test_baidu(self):
driver = self.driver
driver.get(self.base_url+ “/”)
driver.find_element_by_id(“kw”).send_keys(“selenium webdriver”)
driver.find_element_by_id(“su”).click()
driver.close()
def is_element_present(self, how, what):
博客园 — 虫师
http://fnng.cnblogs.com 68
try:self.driver.find_element(by=how, value=what)
except NoSuchElementException, e: return False
return True
def is_alert_present(self):
try:self.driver.switch_to_alert()
except NoAlertPresentException, e:return False
return True
def close_alert_and_get_its_text(self):
try:
alert = self.driver.switch_to_alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: self.accept_next_alert = True
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
if __name__ ==”__main__”:
unittest.main()
加入 unittest 框架后,看上去比我们之前见的脚本复杂了很多,除了中间操作浏览器的几行,
其它都看不懂,不要急,我们来分析一下~!
import unittest
相想使用 unittest 框架,首先要引入 unittest 包,这个不多解释。
class Baidu(unittest.TestCase):
Baidu 类继承 unittest.TestCase 类,从 TestCase 类继承是告诉 unittest 模块的方式,
这是一个测试案例。
def setUp(self):
self.driver = webdriver.Firefox()
博客园 — 虫师
http://fnng.cnblogs.com 69
self.base_url = “http://www.baidu.com/”
setUp 用于设置初始化的部分,在测试用例执行前,这个方法中的函数将先被调用。
这里将浏览器的调用和 URL 的访问放到初始化部分。
self. verificationErrors =[]
脚本运行时,错误的信息将被打印到这个列表中。
self.accept_next_alert =True
是否继续接受下一下警告(字面意思,没找到解释!)
def test_baidu(self):
driver = self.driver
driver.get(self.base_url + “/”)
driver.find_element_by_id(“kw” ).send_keys( “selenium webdriver”)
driver.find_element_by_id(“su” ).click()
test_baidu 中放置的就是我们的测试脚本了,这部分我们并不陌生;因为我们执行的
脚本就在这里。
def is_element_present(self, how, what):
try : self.driver.find_element(by=how, value= what)
except NoSuchElementException, e: return False
return True
is_element_present 函数用来查找页面元素是否存在,在这里用处不大,通常删除。
因为判断页面元素是否存在一般都加在 testcase 中。
def is_alert_present(self):
try:self.driver.switch_to_alert()
except NoAlertPresentException,e: return False
return True
对弹窗异常的处理
def close_alert_and_get_its_text(self):
try:
博客园 — 虫师
http://fnng.cnblogs.com 70
alert = self.driver.switch_to_alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
returnalert_text
finally :self.accept_next_alert =True
关闭警告和对得到文本框的处理,如果不熟悉 python 的异常处理和 if 语句的话,
请去补基础知识,这里不多解释。
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
tearDown 方法在每个测试方法执行后调用,这个地方做所有清理工作,如退出
浏览器等。
self.assertEqual([], self.verificationErrors)
这个是难点,对前面 verificationErrors 方法获得的列表进行比较;如查
verificationErrors 的列表不为空,输出列表中的报错信息。
而且,这个东西,也可以将来被你自己更好的调用和使用,根据自己的需要写入你
希望的信息。(rabbit 告诉我的)
if __name__ == “__main__”:
unittest.main()
unitest.main()函数用来测试 类中以 test 开头的测试用例
这样一一分析下来,我们对 unittest框架有了初步的了解。运行脚本,因为引入了unittest 框
架,所以控制台输出了脚本执行情况的信息。
>>> =========================RESTART ================================
>>>
.
———————————————————————-
Ran 1 test in 10.656s
OK
>>>
博客园 — 虫师
http://fnng.cnblogs.com 71
很帅吧!? 后面将以 unittest 为基础,向新的征程进发~!
二十三、unittest  单元测试框架解析
上一节只是从自动化测试的角度简单分析了一下 unittest ,这一节从 python 的单元测
试框架的角度再学习一下 unittest 框架(又名 PyUnit 框架)
(好好学,这一章整不明白,后面的技术就别玩了!)
widget.py—被测试类
#coding= utf-8
# 将要被测试的类
class Widget:
def __init__(self, size = (40, 40)):
self._size= size
def getSize(self):
return self._size
def resize(self, width, height):
ifwidth < 0 or height < 0:
raise ValueError, “illegal size”
self._size= (width, height)
def dispose(self):
pass
auto.py—测试类
#coding= utf-8
from widget import Widget
import unittest
# 执行测试的类
class WidgetTestCase(unittest.TestCase):
博客园 — 虫师
http://fnng.cnblogs.com 72
def setUp(self):
self.widget = Widget()
def testSize(self):
self.assertEqual(self.widget.getSize(), (40, 40))
def tearDown(self):
self.widget = None
# 构造测试集
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase(“testSize”))
return suite
# 测试
if__name__ == “__main__”:
unittest.main(defaultTest = ‘suite’)
 用 import 语句引入 unittest 模块
 让所有执行测试的类都继承于 TestCase 类,可以将 TestCase 看成是对特定类进行
测试的方法的集合
 setUp()方法中进行测试前的初始化工作,tearDown()方法中执行测试后的清除工
作。setUp()和 tearDown()都是 TestCase 类中定义的方法
 在 testSize()中调用 assertEqual()方法,对 Widget 类中 getSize()方法的返
回值和预期值进行比较,确保两者是相等的,assertEqual()也是 TestCase 类中
定义的方法。
 提供名为 suite()的全局方法,PyUnit 在执行测试的过程调用 suit()方法来确定
有多少个测试用例需要被执行,可以将 TestSuite 看成是包含所有测试用例的一个容
器。
框架分析
软件测试中最基本的组成是单元测试用例(testcase),我们在实际测试过程中,不
可能真对一个功能(类)只写一个用例。TestCase 在 PyUnit 测试框架中被视为测试单
元的运行实体,Python 程序员可以通过它派生自定义的测试过程与方法(测试单元),利
博客园 — 虫师
http://fnng.cnblogs.com 73
用 Command 和 Composite 设计模式,多个 TestCase 还可以组合成测试用例集合。
编写测试用例
采用 PyUnit 提供的动态方法,只编写一个测试类来完成对整个软件模块的测试,这样
对象的初始化工作可以在 setUp()方法中完成,而资源的释放则可以在 tearDown()方法中完
成。
对的 widget.py 被测试类的多方法进行测试
# 执行测试的类
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget()
# 测试 getSize()方法的测试用例
def testSize(self):
self.assertEqual(self.widget.getSize(), (40, 40))
# 测试 resize()方法的测试用例
def testResize(self):
self.widget.resize(100, 100)
self.assertEqual(self.widget.getSize(), (100, 100))
def tearDown(self):
self.widget.dispose()
self.widget = None
我们可以在一个测试类中,写多个测试用例对被测试类的方法进行测试。
组织用例集
完整的单元测试很少只执行一个测试用例,开发人员通常都需要编写多个测试用例才能
对某一软件功能进行比较完整的测试,这些相关的测试用例称为一个测试用例集,在
PyUnit 中是用 TestSuite 类来表示的。
可以在单元测试代码中定义一个名为 suite()的全局函数,并将其作为整个单元测试
博客园 — 虫师
http://fnng.cnblogs.com 74
的入口,PyUnit 通过调用它来完成整个测试过程。
def suite():
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase(“testSize”))
suite.addTest(WidgetTestCase(“testResize”))
return suite
如果用于测试的类中所有的测试方法都以 test 开头,Python程序员甚至可以用
PyUnit 模块提供的 makeSuite()方法来构造一个。
def suite():
return unittest.makeSuite(WidgetTestCase, “test”)
TestSuite 类可以看成是 TestCase 类的一个容器,用来对多个测试用例进行组织,这样多
个测试用例可以自动在一次测试中全部完成。
运行测试集
PyUnit 使用 TestRunner 类作为测试用例的基本执行环境,来驱动整个单元测试过程。
Python 开发人员在进行单元测试时一般不直接使用 TestRunner 类,而是使用其子类
TextTestRunner 来完成测试,并将测试结果以文本方式显示出来:
runner= unittest.TextTestRunner()
runner.run(suite)
对 widget.py 被测试类,下面通过 PyUnit 编写完整的单元测试用例:
text_runner.py
#coding=utf-8
from widget import Widget
import unittest
# 执行测试的类
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget()
def tearDown(self):
self.widget.dispose()
self.widget = None
博客园 — 虫师
http://fnng.cnblogs.com 75
def testSize(self):
self.assertEqual(self.widget.getSize(), (40, 40))
def testResize(self):
self.widget.resize(100, 100)
self.assertEqual(self.widget.getSize(), (100, 100))
# 测试
if__name__ == “__main__”:
# 构造测试集
suite = unittest.TestSuite()
suite.addTest(WidgetTestCase(“testSize”))
suite.addTest(WidgetTestCase(“testResize”))
# 执行测试
runner= unittest.TextTestRunner()
runner.run(suite)
PyUnit 模块中定义了一个名为 main 的全局方法,使用它可以很方便地将一个单元测
试模块变成可以直接运行的测试脚本,main()方法使用 TestLoader 类来搜索所有包含
在该模块中的测试方法,并自动执行它们。如果 Python 程序员能够按照约定(以 test
开头)来命名所有的测试方法,那就只需要在测试模块的最后加入如下几行代码即可:
#coding=utf-8
from widget import Widget
import unittest
# 执行测试的类
class WidgetTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget()
def tearDown(self):
self.widget.dispose()
self.widget = None
def testSize(self):
self.assertEqual(self.widget.getSize(), (40, 40))
def testResize(self):
self.widget.resize(100, 100)
self.assertEqual(self.widget.getSize(), (100, 100))
# 测试
博客园 — 虫师
http://fnng.cnblogs.com 76
if__name__ == “__main__”:
unittest.main()
二十四、批量执行测试集
有了上面对 unittest 框架的学习作铺垫,下面我们就可以将多个自动化用例用到一起执
行。
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.uiimport Select
from selenium.common.exceptions import NoSuchElementException
import unittest, time, re
class Baidu(unittest.TestCase):
def setUp(self):
self.driver =webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url = “http://www.baidu.com/”
self.verificationErrors = []
self.accept_next_alert = True
#百度搜索用例
def test_baidu_search(self):
driver = self.driver
driver.get(self.base_url +”/”)
driver.find_element_by_id(“kw”).send_keys(“selenium webdriver”)
driver.find_element_by_id(“su”).click()
time.sleep(2)
driver.close()
#百度设置用例
def test_baidu_set(self):
driver = self.driver
博客园 — 虫师
http://fnng.cnblogs.com 77
#进入搜索设置页
driver.get(self.base_url +”/gaoji/preferences.html”)
#设置每页搜索结果为 100 条
m=driver.find_element_by_name(“NR”)
m.find_element_by_xpath(“//option[@value=’100′]”).click()
time.sleep(2)
#保存设置的信息
driver.find_element_by_xpath(“//input[@value=’保存设置’]”).click()
time.sleep(2)
driver.switch_to_alert().accept()
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
if__name__ == “__main__”:
unittest.main()
虽然已经实例了多个用例一起跑,但这样仍然不合理,几个用例一起执行还好,如果几
十个、几百个的用例的话,这个文件将变得无比庞大,不利于维护。
所以,做合理的做法是一个例一个文件,把所文件放一个文件夹下,通过单独的脚本控
制所有用例的执行,将脚本的执行结果输出到一个 log 文件中。
初步把框架走通了。
单个用例相信你早就会写了,把他们整理一下放到一个文件夹下,然后编写执行用例集
的脚本:
博客园 — 虫师
http://fnng.cnblogs.com 78
test_case_.py
#-*-coding=utf-8-*-
import os
#列出某个文件夹下的所有 case,这里用的是 python,所在 py 文件运行一次后会生成一个 pyc
的副本
caselist=os.listdir(‘D:\\selenium_use_case\\test_case’)
for a in caselist:
s=a.split(‘.’)[1:][0] #选取所要执行的用例
ifs==’py’:
#此处执行 dos 命令并将结果保存到log.txt
os.system(‘D:\\selenium_use_case\\test_case\\%s 1>>log.txt 2>&1’%a)
查看 log.txt 文件:
..
———————————————————————-
Ran 2 tests in 32.469s
OK
..
———————————————————————-
Ran 2 tests in 27.016s
OK
二十五、异常捕捉与错误截图
创建错误截图文件夹,目录结果如下:
用例不可能每一次运行都成功,肯定运行时候有不成功的时候,换句话说,我们不需要
永远都运行成功的用例,他本身是没有什么意义的。关键是我们捕捉到错误,并以把并错误
博客园 — 虫师
http://fnng.cnblogs.com 79
截图保存,这将是一个非常棒的功能,也会给我们错误定位带来方便。
baidu.py
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.uiimport Select
from selenium.common.exceptions import NoSuchElementException
import unittest, time, re
class Baidu(unittest.TestCase):
def setUp(self):
self.driver =webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url = “http://www.baidu.com/”
self.verificationErrors = []
self.accept_next_alert = True
#百度搜索用例
def test_baidu_search(self):
driver = self.driver
driver.get(self.base_url +”/”)
try:
#kwddd 是一个无法找到的元素id
driver.find_element_by_id(“kwdddd”).send_keys(“selenium webdriver”)
except:
driver.get_screenshot_as_file(“D:\\selenium_use_case\\error_png\\kw.png”)
#如果没有找到上面的元素就截取当前页面。
driver.find_element_by_id(“su”).click()
time.sleep(2)
driver.close()
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
if__name__ == “__main__”:
unittest.main()
这里特意把脚本写错误的,使脚本找不到 id 为 kwddd 的元素,通过 try….except…对
博客园 — 虫师
http://fnng.cnblogs.com 80
异常进行捕捉;并把结果保存下来。再次执行你的脚本会发现 error_png 目录下面产生了
错误时候的截图。
截图函数 get_screenshot_as_file
selenium.webdriver.remote.webdriver.get_screenshot_as_file(filename)
截图当前窗口图片。如果有任何 IOError 将返回 false ,否则将返回 Ture .
filename: 指定错误截图的存放路径及图片名。
用法:
driver.get_screenshot_as_file(’/Screenshots/foo.png’)
我们需要用 python这门语言去调用 selenium 的一些工具来操作浏览器,帮助我们实现
“web UI ”自动化。
===================华丽分割线======================
下面的内容为本文档第三版的内容,后面的学习重点就不是通过 webdriver如何操作页
面元素了,我们的关注点将转移到框架上,如何 python 语言使我们的框架实现更强大的功
能,我在后面的章节学习与整理的过程中,也补充了不少 python 知识,建议读者最好掌握
一些 python 编程基础。
博客园 — 虫师
http://fnng.cnblogs.com 81
二十六、生成测试报告(HTMLTestRunner)
在脚本云行完成之后,除了在 log.txt 文件看到运行日志外,我们更希望能生一张漂亮
的测试报告来展示用例执行的结果。
下面我们就通过 HTMLTestRunner.py 来生成测试报告。
首先要下 HTMLTestRunner.py 文件,下载 地址:
http://tungwaiyip.info/software/HTMLTestRunner.html
将下载的文件放入…\Python27\Lib 目录下(windows),打开交互模式引入包,如果没有
报错,说明添加成功,当然也可以通过 dir() 看看’HTMLTestRunner 包含发哪些方法。
>>> importHTMLTestRunner
>>> dir(HTMLTestRunner)
[‘HTMLTestRunner’, ‘OutputRedirector’, ‘StringIO’, ‘Template_mixin’, ‘TestProgram’, ‘TestResult’,
‘_TestResult’, ‘__author__’, ‘__builtins__’, ‘__doc__’, ‘__file__’, ‘__name__’, ‘__package__’,
‘__version__’, ‘datetime’, ‘main’, ‘saxutils’, ‘stderr_redirector’, ‘stdout_redirector’, ‘sys’, ‘time’,
‘unittest’]
>>>
ok ! 下面在我们用例中添加可以生成报告的代码:
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.uiimport Select
from selenium.common.exceptions import NoSuchElementException
import unittest, time, re
import HTMLTestRunner
class Baidu(unittest.TestCase):
博客园 — 虫师
http://fnng.cnblogs.com 82
def setUp(self):
self.driver =webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url = “http://www.baidu.com”
self.verificationErrors = []
self.accept_next_alert = True
#测试用例一
def test_baidu_search(self):
#测试用例二
def test_baidu_set(self):
#测试用例三
def test_baidu_xxx(self):
….
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)if __name__ == “__main__”:
if__name__ == “__main__”:
testunit=unittest.TestSuite() #定义一个单元测试容器
testunit.addTest(Baidu(“test_baidu_search”)) #将测试用例加入到测试容器中
testunit.addTest(Baidu(“test_baidu_set”))
testunit.addTest(Baidu(“test_baidu_xxx”))
filename = ‘D:\\result.html’ #定义个报告存放路径,支持相对路径。
博客园 — 虫师
http://fnng.cnblogs.com 83
fp = file(filename, ‘wb’)
runner =HTMLTestRunner.HTMLTestRunner(
stream=fp,
title=’Report_title’,
description=’Report_description’)
runner.run(testunit) #自动进行测试
代码分析:
用例的部分通过之前的学习已经非常了解了,下面重点分析底部这段代码:
testunit=unittest.TestSuite() #定义一个单元测试容器
testunit.addTest(Baidu(“test_baidu_search”)) #将测试用例加入到测试容器中
testunit.addTest(Baidu(“test_baidu_set”))
testunit.addTest(Baidu(“test_baidu_xxx”))
filename = ‘D:\\result.html’ #定义个报告存放路径,支持相对路径。
fp = file(filename, ‘wb’)
runner =HTMLTestRunner.HTMLTestRunner(
stream=fp,
title=’Report_title’,
description=’Report_description’)
runner.run(testunit) #自动进行测试
TestSuite 其实并不陌生,在 23 章 章 unittest 单元测试框架分析的部分已经介绍,只是为
了方便我们使用了 unittest.main() 方法,默认会将所有用例执行。因为这里要生成报告,所
以要将所有用例列出;
博客园 — 虫师
http://fnng.cnblogs.com 84
下面的也很容易理解,创建 result.html 文件,给以读写权限(wb),调用HTMLTestRunner
文件,并将测试结果以HTMLTestRunner 规定的格式通过fp 传递写入到result.html 文件中。
最后是运行的 testunit ,也就是 TestSuite 中的所有用例。
脚本运行结束,生成如下报告:
问题:
这个报告是根据一个.py 文件生成的,这样就迫使我们把所有用例都写在一个.py 文件
里,如果我们每一个用例都写在不同的.py 文件里将生成很多个报告,不便于阅读;但写在
一个.py 文件里,如果用例非常多的话,同样不便于维护。
后面,我们将一起寻求解决办法。
二十七、数据驱动测试
先来理解一下自动化领域的两种驱动,对象驱动与数据驱动。
数据驱动:测试数据的改变引起执行结果的改变 叫 数据驱动;
关键字驱动:测试对象名字的改变起引起测试结果的改变 叫 关键字驱动。
27.1 、读取文件参数化
博客园 — 虫师
http://fnng.cnblogs.com 85
以百度表搜索为例,我们可以通过脚本循环执行,读取一文件中不同的内容来完成自动
化工作,也就是说我们每次取的文件里的搜索关键字不同,而每次百度搜索的的结果不同,
这也是数据驱动的本质。
代码如下:
d:\abc\data.txt
baidu_read_data.py
#coding=utf-8
from selenium import webdriver
import os,time
source= open(“D:\\abc\\data.txt”, “r”)
values = source.readlines()
source.close()
# 执行循环
for serch in values:
browser =webdriver.Firefox()
browser.get(“http://www.baidu.com”)
browser.find_element_by_id(“kw”).send_keys(serch)
browser.find_element_by_id(“su”).click()
browser.quit()
这里简单说明一下,open方法左以只读方式(r)打开本地的 data.txt 文件,readlines
方法是逐行的读取文件内容。
通过 for 循环,serch 可以每次获取到文件中的一行数据,在定位到百度的输入框后,
博客园 — 虫师
http://fnng.cnblogs.com 86
将数据传入 send_keys(serch)。这样通过循环调用,直到文件的中的所有内容全被读取。
27.2 、用户名密码的参数化(读取文件)
按照上面的方法,对自动化脚本中用户名密码进行参数化应该很简单,其实没有想象的
那么简单,从目前我所查到 python 读取方法有,整个文件读取,逐行读取,固定字节读取。
怎样才一次读取用户名和密码两个信息呢,最初的修改是这样的:
创建两个文件,分别存放用户名密码
调用用户名密码登录登录的脚本
#coding=utf-8
from selenium import webdriver
import os,time
source= open(“D:\\abc\\data2.txt”, “r”) #用户名文件
user = source.read(5) #用户名长度
source.close()
source2 = open(“D:\\abc\\data3.txt”, “r”) #密码文件
pw =source2.read(6) #密码长度
source2.close()
driver = webdriver.Firefox()
driver.get(“http://passport.kuaibo.com/login/?referrer=http%3A%2F%2Fwebcloud.kuaibo.com%2
F”)
driver.find_element_by_id(“user_name”).clear()
driver.find_element_by_id(“user_name”).send_keys(user)
time.sleep(3)
driver.find_element_by_id(“user_pwd”).clear()
driver.find_element_by_id(“user_pwd”).send_keys(pwd)
time.sleep(3)
博客园 — 虫师
http://fnng.cnblogs.com 87
driver.find_element_by_name(“Submit”).click()
time.sleep(1)
driver.quit()
缺点:
虽然目的达到了这,但这样的实现有很多问题:
1、用户名密码分别在不同的文件里,这样就要求用户名密码必须一一对应
2、必须指定读取的长度,测试 readlines() 并不是读取的一行数据。
3、无法循环读取。
27.3 、用户名的参数化(字典)
 用户名密码参数化
 解决循环调用
通过一整天研究,重新补习 python 字典、函数调用,如果固定只是读取用户名,密码
两个值,可以通过如下方法实现。
创建 fun.py 文件,定义一个字典方法:
def zidian():
d={‘fnngj’:’a23456′,’testing360′:123456}
print “suess read usernameand password!!”
return d
字典的可以方便的存放 k,v键值对,一个键对应一个值;注意,如果密码中有非数字,
需要加单引号。
下面循环调用词典的值:
#coding=utf-8
from selenium import webdriver
import os,time
import fun #导入函数
博客园 — 虫师
http://fnng.cnblogs.com 88
#循环调用字典里的用户名密码,分别赋值给 k,v
for k,v in fun.zidian().items():
driver = webdriver.Firefox()
driver.get(“http://passport.kuaibo.com/login/?referrer=http%3A%2F%2Fwebcloud.kuaibo
.com%2F”)
driver.find_element_by_id(“user_name”).clear()
driver.find_element_by_id(“user_name”).send_keys(k)
time.sleep(3)
driver.find_element_by_id(“user_pwd”).clear()
driver.find_element_by_id(“user_pwd”).send_keys(v)
time.sleep(3)
driver.find_element_by_id(“dl_an_submit”).click()
time.sleep(1)
driver.close()
脚本这样表设计就稳定了很多,每次取的值非常固定,而且同样实现了参数与脚本分离,
如果几百个脚本都调用 fun( ) 函数,当需要修改用户名密码时,只用修改 fun( )函数里面字
典的值就可以了。
27.4 、用户名密码的参数化(函数)
其实,在我的项目中只需要做到参数化就行了,并不需要循环的读取内容。那么通过函
数调用就可以很简单的解决。
fun.py
def user(k=’fnngj’,v=123456):
print “suess read username and password!!”
return k,v
赋默认值,并将结果返回。
调用函数值:
#coding=utf-8
博客园 — 虫师
http://fnng.cnblogs.com 89
from selenium import webdriver
import os,time
import fun #导入函数
#通过调用函数获得用户名&密码
k,v = fun.user()
print k,v
driver = webdriver.Firefox()
driver.get(“http://passport.kuaibo.com/login/?referrer=http%3A%2F%2Fwebcloud.kuaibo
.com%2F”)
driver.find_element_by_id(“user_name”).clear()
driver.find_element_by_id(“user_name”).send_keys(k)
driver.find_element_by_id(“user_pwd”).clear()
driver.find_element_by_id(“user_pwd”).send_keys(v)
driver.find_element_by_id(“dl_an_submit”).click()
time.sleep(3)
driver.close()
运行结果:
>>> =================== RESTART ================================
>>>
suess read username and password!!
fnngj 123456
.
———————————————————————-
Ran 1 test in 25.484s
OK
博客园 — 虫师
http://fnng.cnblogs.com 90
如果学好了 python 语言,解决问题的方法是多样的,使用最贴合需求的方法,简单解
决问题。这一节写的比较多,对构建自动化框架来说,参数化是非常重要的一个知识点。
二十八、测试套件
在 23 章单元测试框架解析中我提了到“测试套件”,当时只是把一个.py 文件里的多个
用例通过测试套件执行。批量执行测试集中 虽然可以批量执行多个.py 文件,但它使用的
是读取文件夹下文件的方式,而不是使用的测试套件。这一节就使用测试套件来执行多个.py
测试文件。
最终我们全通过测试套件完成下面的结构:
测试套件的问题解决了,26 章生成测试报告遗留的问题自然也可以解决了。
28.1 、测试套件实例
下面通过一个例子来组建我们的测试套件。
test_youdao.py
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
博客园 — 虫师
http://fnng.cnblogs.com 91
from selenium.webdriver.support.uiimport Select
from selenium.common.exceptions import NoSuchElementException
import unittest, time, re
import HTMLTestRunner
class Youdao(unittest.TestCase):
def setUp(self):
self.driver =webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url = “http://www.baidu.com”
self.verificationErrors = []
self.accept_next_alert = True
#百度搜索用例
def test_youdao_search(self):
driver = self.driver
driver.get(self.base_url +”/”)
try:
driver.find_element_by_id(“query”).send_keys(u”虫师”)
driver.find_element_by_id(“qb”).click()
time.sleep(2)
except:
driver.get_screenshot_as_file(“D:\\selenium_use_case\\error_png\\kw.png”)
#如果没有找到上面的元素就截取当前页面。
def tearDown(self):
self.driver.quit()
博客园 — 虫师
http://fnng.cnblogs.com 92
self.assertEqual([], self.verificationErrors)
if__name__ == “__main__”:
suite = unittest.TestSuite()
suite.addTest(Youdao(“test_youdao_search”))
#这里可以添加更多的用例,如:
#suite.addTest(Youdao(“aaaa”))
unittest.TextTestRunner().run(suite)
test_baidu.py
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.uiimport Select
from selenium.common.exceptions import NoSuchElementException
import unittest, time, re
import HTMLTestRunner
class Baidu(unittest.TestCase):
def setUp(self):
self.driver =webdriver.Firefox()
self.driver.implicitly_wait(30)
self.base_url = “http://www.baidu.com”
self.verificationErrors = []
self.accept_next_alert = True
博客园 — 虫师
http://fnng.cnblogs.com 93
#百度搜索用例
def test_baidu_search(self):
driver = self.driver
driver.get(self.base_url +”/”)
try:
#是一个无法找到的元素 id
driver.find_element_by_id(“kw”).send_keys(“selenium webdriver”)
except:
driver.get_screenshot_as_file(“D:\\selenium_use_case\\error_png\\kw.png”)
#如果没有找到上面的元素就截取当前页面。
driver.find_element_by_id(“su”).click()
time.sleep(2)
driver.close()
def tearDown(self):
self.driver.quit()
self.assertEqual([], self.verificationErrors)
if__name__ == “__main__”:
suite = unittest.TestSuite()
suite.addTest(Baidu(“test_baidu_search”))
#同样的,可以在这个文件中添加更多的用例。
#suite.addTest(Youdao(“aaaa”))
results= unittest.TextTestRunner().run(suite)
通过测试套件运行上面两个测试文件,创建 all_tests.py 文件
博客园 — 虫师
http://fnng.cnblogs.com 94
#coding=utf-8
“Combine tests for gnosis.xml.objectify package(req 2.3+)”
import unittest, doctest
import test_baidu, test_youdao #这里需要导入测试文件
import HTMLTestRunner
suite = doctest.DocTestSuite()
#罗列要执行的文件
suite.addTest(unittest.makeSuite(test_baidu.Baidu))
suite.addTest(unittest.makeSuite(test_youdao.Youdao))
unittest.TextTestRunner(verbosity=2).run(suite)
运行结果:
>>> =========================RESTART ================================
>>>
test_baidu_search (test_baidu.Baidu) … ok
test_youdao_search (test_youdao.Youdao) … ok
———————————————————————-
Ran 2 tests in 15.140s
OK
28.2 、整合 HTMLTestRunner  测试报告
生成 HTMLTestRunner 报告和前面的方法一样,只是把代码移动 all_tests.py 文件即可;
而且只需要在这一个地方生成即可。
下面看加入 HTMTestRunner之后的 all_tests.py 文件
博客园 — 虫师
http://fnng.cnblogs.com 95
#coding=utf-8
“Combine tests for gnosis.xml.objectify package(req 2.3+)”
import unittest, doctest
#这里需要导入测试文件(test_baidu.py,test_youdao.py)
import test_baidu,test_youdao
import HTMLTestRunner
suite = doctest.DocTestSuite()
suite.addTest(unittest.makeSuite(test_baidu.Baidu))
suite.addTest(unittest.makeSuite(test_youdao.Youdao))
filename = ‘D:\\result20.html’
fp = file(filename, ‘wb’)
runner=HTMLTestRunner.HTMLTestRunner(
stream=fp,
title=’Report_title’,
description=’Report_description’)
runner.run(suite)
代码都是前面见过的,这里就不费口舌了再解析了;如果不太理解就多敲几遍,自然就
理解。运行测试报告如下,这样再多文件的用例都可以放到一张报告里了。
博客园 — 虫师
http://fnng.cnblogs.com 96
28.3 、更易读的报告
报告已经接近完美了,唯一的一点小瑕疵,这报告如果给领导看的话,哪知道什么是什
么,经过 MarkRabbit 的指点,我们可以给每一个用例加个中文注释。
…………
#百度搜索用例
def test_baidu_search(self):
u”””百度搜索用例”””
driver = self.driver
driver.get(self.base_url +”/”)
…………….
每个用例(方法)下面都可以加这个一行注释信息,小 u 是避免中文引起的乱码问题。
博客园 — 虫师
http://fnng.cnblogs.com 97
再来跑一下用例,找开生成的报告,是不是完美了,傻瓜都知道是干嘛的。
二十九、结构改进
到目前为止问题已经解决了,通过测试套件执行所有用例,测试报告整合,所有文件都
在一个目录下面,估计用例写多了,不方便管理,这一章试着调整一下结构。
29.1 、all_tests.py  移出来
all_tests.py 是调用例的程序,而不是执行用例的,所以应该把它移出来。结构上会
更为合理。
/selenium_use_case/test_case/untie/test_baidu.py
/unite/test_baidu.py
/unite/__init__.py
/unite/…
/all_tests.py
目录结构应该是这样的,untie 文件夹下存放具体的执行用例,all_tests.py 应该与 untie
文件夹平级。
另外,需要在 unite 文件夹下放一个__init__.py 文件,文件内容可以为空。
那么直接移出来后再运行 all_tests.py 文件会提示不到测试文件,所以,我们要对代码
做调整,把文件夹加到 sys.path 下就可以找到了。在 all_tests.py 头部添加以下内容。
…….
博客园 — 虫师
http://fnng.cnblogs.com 98
import sys
sys.path.append(“/selenium_use_case/test_case”)
from sutie import test_youdao
from sutie import test_baidu
…..
29.2 、__init__.py  文件解析
all_tests.py 是移出来了,但是还有个问题,导入包(用例文件)也是个问题,假如几个
用例可以通过“from sutie import test_xxx” 的方式导入,假如成几百条呢,这样罗列几百
条,做法确实太二;那有没有不那么二的方式呢。
还记得上面提到的__init__.py 文件吧,这文件是干嘛的,为什么要在引用的目录下加这
个文件?
要弄明白这个问题,首先要知道,python 在执行 import 语句时,到底进行了什么操作,按照
python 的文档,它执行了如下操作:
第 1 步,创建一个新的,空的 module 对象(它可能包含多个 module);
第 2 步,把这个 module 对象插入 sys.module 中
第 3 步,装载 module 的代码(如果需要,首先必须编译)
第 4 步,执行新的 module 中对应的代码。
在执行第 3 步时,首先要找到 module 程序所在的位置,搜索的顺序是:
当前路径 (以及从当前目录指定的 sys.path),然后是 PYTHONPATH,然后是 python 的安
装设置相关的默认路径。正因为存在这样的顺序,如果当前 路径或 PYTHONPATH 中存在与标准
module 同样的 module,则会覆盖标准 module。也就是说,如果当前目录下存在 xml.py,那么
执 行 import xml 时,导入的是当前目录下的 module,而不是系统标准的 xml。
了解了这些,我们就可以先构建一个 package,以普通 module 的方式导入,就可以直接访问
此 package 中的各个 module 了。python 中的 package 必须包含一个__init__.py 的文件。
——以上引用“老王 python”
博客园 — 虫师
http://fnng.cnblogs.com 99
其实__init__.py 文件中可以有内容; 我们在导入一个包时,实际上导入了它的
__init__.py 文件。
在__init__.py 文件中添加导入包
import test_baidu
import test_youdao
然后,all_tests.py 文件可是这样修改:
…….
import sys
sys.path.append(“/selenium_use_case/test_case”)
from sutie import *
…..
“*” 星号表示导入 sutie 目录下的所有文件;在 sutie 目录下创建测试用例文件,只用
在__init__.py 文件下罗列就可以了。而对于 all_tests.py 文件来说不需要做任何调整。
29.3 、调用多级目录的用例
当测试用例达到一定量级的时候,为了便于管理,必定需要在目录下面再分目录。假设
我们有这样一个结构:
/selenium_use_case/test_case/untie/test_baidu.py
/unite/test_baidu.py
/unite/sogou/test_sogou.py —-二级测试用例目录
/unite/sogou/__init__.py
/unite/sogou/…
/unite/__init__.py
/unite/…
/all_tests.py
其实,这个问题也很好处理,接着分析__init__.py 文件,它处了能导入当前目录下的
博客园 — 虫师
http://fnng.cnblogs.com 100
文件,是不是还可以导入其它目录下包,或者模块。假设在/unite/sogou/目录下创建了
test_sogou.py 测试文件,修改 unite 目录__init__.py 文件:
#coding=utf-8
import sys
sys.path.append(“/selenium_use_case/test_case/sutie”)
from sogou import *
import test_baidu
import test_youdao
别忘了/unite/sogou/ 目录下也要加__init__.py 文件,并且加入包。掌握的这个技巧,再
也不用担心多级目录的问题了。
29.4 、改进用例的读取
你以为这样就算完了么? 还有个更严峻的问题需要处理,如果你够警觉一定注意
all_tests.py 的这段代码:
….
suite = doctest.DocTestSuite()
suite.addTest(unittest.makeSuite(test_baidu.Baidu))
suite.addTest(unittest.makeSuite(test_youdao.Youdao))
suite.addTest(unittest.makeSuite(test_sogou.Sogou))
…..
对的,这也是无法回避的一个硬伤,跟导入包一样无法避免,想想成百的用例罗列到这
里是多么痛的领悟。。
最先想到的是能不能通过一个循环来解决掉这个问题,循环的读取某个目录下的所有文
件;如果你还记得本文档的  第 24章 章 有一个叫 test_case_.py 的文件的话,读取某个文件夹
下的所有文件是一件很简单的事情;但是如何将结果生成到报告里呢。解决这个问题还是稍
微有那么一点儿难度的。
经过改进的新 all_tests.py 代码如下:
博客园 — 虫师
http://fnng.cnblogs.com 101
#coding=utf-8
import sys ,re ,os,math
sys.path.append(“/selenium_use_case/test_case”)
from sutie import *
import unittest, doctest ,site
import HTMLTestRunner
#将用例组建成数组
alltestnames = [
‘sutie.test_baidu.Baidu’,
‘sutie.test_youdao.Youdao’,
‘sutie.sogou.test_sogou.Sogou’, #注意这个用例是二级目录下的
]
suite =unittest.TestSuite()
if __name__== ‘__main__’:
# 这里我们可以使用 defaultTestLoader.loadTestsFromNames(),
# 但如果不提供一个良好的错误消息时,它无法加载测试
# 所以我们加载所有单独的测试,这样将会提高脚本错误的确定。
fortest inalltestnames:
try:
#最关键的就是这一句,循环执行数据数的里的用例。
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(test))
except Exception:
print ‘ERROR: Skipping tests from “%s”.’ % test
try:
__import__(test)
except ImportError:
print ‘Could not importthetest module.’
else:
print ‘Could not load the test suite.’
from traceback import print_exc
print_exc()
print
print ‘Running the tests…’
filename = ‘D:\\result21.html’
博客园 — 虫师
http://fnng.cnblogs.com 102
fp = file(filename,’wb’)
runner =HTMLTestRunner.HTMLTestRunner(
stream=fp,
title=’Report_title’,
description=’Report_description’)
runner.run(suite)
代码解析,为了做到只解决当前面的问题,上面的代码做了很多简化。其实,我们可以
在这里完成更多的功能。
首先我们以“目录.用例文件.用例类”的格式将用例放到一个数组中,可以这样做的前
提是我们导入了测试用例文件;然后组成了 alltestnames 数组。
通过一个 for 循环来读取数组的内容;读取的方法是:
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(test))
紧接的 try…except… 是对异常的处理,如果不理解,可以暂无视或删除异常捕捉
的相关代码,使代码更清爽。
下面的代码已经见过好多次了,是用于生成 HTMLTestRunner 报告的。
29.5 、进一步分离用例列表
都到进一步分,下面知道该怎么做了吧!? 翻一下  第 27.3节 节 参数化中的字典,应该能
找到方法。都把用例组成数组了,我们要做的就是把它放到一个单独的文件里。
创建 allcase_list.py 文件,与 all_tests.py 在同一级目录下。把数组放到一个方法
里,allcase_list.py 内容如下:
def caselist():
alltestnames = [
‘sutie.test_baidu.Baidu’,
‘sutie.test_youdao.Youdao’,
‘sutie.sogou.test_sogou.Sogou’,
]
print “suess read case list success!!”
博客园 — 虫师
http://fnng.cnblogs.com 103
return alltestnames
在 all_tests.py 中进行调用:
#coding=utf-8
….
import allcase_list #调用数组文件
#获取数组方法
alltestnames = allcase_list.caselist()

suite =unittest.TestSuite()
if __name__== ‘__main__’:
fortest inalltestnames:
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(test))
…..
现在现在优雅多了,把需要的执行的用例往 allcase_list.py 的数组是罗列就行了。
用例的调整,all_tests.py 文件不需要做任何的修改。
最后再回顾一下我们有目前测试的目录结构:
/selenium_use_case/test_case/untie/test_baidu.py —–一级目录测试用例
/unite/test_baidu.py
/unite/sogou/test_sogou.py —-二级目录测试用例目录
/unite/sogou/__init__.py
/unite/sogou/…
/unite/__init__.py
/unite/…
/all_tests.py —-调用所有脚本执行
/allcase_list.py —–罗列要执行的用例
/test_result/result1.html —-测试报告的存入目录
博客园 — 虫师
http://fnng.cnblogs.com 104
目前看上去还不错的样子~!(得意笑),但是我们项目不同,需求不同,或者当用例达
到一定量级后,还会有很多问题暴露出来,需要我们一一的去解决;好吧~!第三版的内容
就到这里了。
三十、UliPad–python  开发利器
工欲善其事,必先利其器
有时候往往选择太多,变得无从选择。如果你在 python 开发中已经找到了趁手的 IDE这
一节可以无视。
其实,pyhon下面能找到一款不错的开发工具是不太容易的。
IDLE 写写单个小程序很好,但一个程序文件与执行信息是两个窗口,程序开多了就分
不清哪个了。
pythonWin 也用过,窗口有些老土,窗口布局我不会设置,所以觉得也不好用。
notepad++ 这种小巧的万能编辑器,偶尔用用还行。
x linux 会有一些非常不错的交互式python IDE ,如 ipython、bpython 等。
m vim 肯定是开发神器,但一般也只有高手才会运用自如,体会它的奥妙。
UliPad 是找到的写 python 最舒服的一个 IDE 。
地址: https://code.google.com/p/ulipad/
免费,可以免费获得并使用它的所有功能。
支持 windows 、MAC、linux 等平台。
小巧,内存占用很少,10MB 左右。
博客园 — 虫师
http://fnng.cnblogs.com 105
具体,的安装使用,这里就不介绍了,不是本文档的主题。有兴趣使用可以参考我的博客:
http://www.cnblogs.com/fnng/p/3393275.html
另外,还有一些非常棒的收费 python IDE
Wing IDE4.1
http://wingware.com/
pycharm
http://www.jetbrains.com/pycharm/
希望这一节没影响到文档的和谐。呵呵~!
博客园 — 虫师
http://fnng.cnblogs.com 106
后记:
都在谈自动化测试,自动化测试是“部分”功能测试的一种替代技术(它们比例肯定在
逆转)。通过学习自动脚本也可以使测试人员突破不懂代码的限制;而自动化脚本入门简单。
我觉得自动化是方向。
关于自动化又帮了你一段路,但是,依然还有很多问题没有解决;比如,测试用例的多
线程处理。目前的结构还不够完美,在脚本运行中,我们可以捕捉更多的信息,更容易的定
位问题;使我们的结构更灵活的适应需求的变化;路还很长,任重道远,一起加油吧!
这些问题依然不是一份学习文档可以解决的,如果你掌握了本文档的所有内容,建议从
以下几个方面来提高自己的自动化测试水平:
python 语言:兔子(它不让叫兔子了,叫 t MarkRabbit )的话清晰的说明了学习自动
化测试的思路:我们需要用 python 这门语言去调用 selenium 的一些工具来操作浏览器,
帮助我们实现“web UI ”自动化。所以,我们的重心应该放在语言本身的学习。后面这几
章解决问题用的也是 python 技术。
Javascript 语言:在实际的自动化测试过程中,我们会遇到各种问题,有时候 webdriver
提供的方法不能帮我们解决问题,那么需要借助 Javascript 来解决问题。
xpath \css 定位: 不能操作一个元素,很多情况下是我们没办法定位这个元素;所以要
深入了解 xpath \css 定位的用法。
扩展资料:
rtsm eyiselenium 与webdriver 的关系:
http://v.qq.com/boke/page/j/v/v/j01135krrvv.html
lazyman 快速入门:
http://v.qq.com/boke/page/i/k/a/i0113wompka.html
关于 python 自动化的博客,慢慢研读:
博客园 — 虫师
http://fnng.cnblogs.com 107
http://www.cnblogs.com/hzhida/archive/2012/08/13/2637089.html
splinter 自动化框架:
http://splinter.cobrateam.info/docs/why.html
http://v.qq.com/boke/page/s/8/3/s0114uu1d83.html。
大家可以了解一下webdriverguide的内容
webdriverAPI地址:
https://github.com/easonhan007/webdriver_guide
robot framework
自动化测试框架,后序研究。
RF框架系列文章
http://www.51testing.com/?21116/
http://blog.csdn.net/tulituqi/article/category/897484/2
安装:http://blog.sina.com.cn/s/blog_654c6ec70100tkxn.html
selenium webdriver py 文档
http://selenium.googlecode.com/git/docs/api/py/index.html
r seleniumwrapper  0.5.3
https://pypi.python.org/pypi/seleniumwrapper
selenium webdriver 系列教程
http://blog.csdn.net/nbkhic/article/details/6896889
文档
http://selenium.googlecode.com/git/docs/api/py/index.html
phantomJS

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,086
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,561
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,410
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,183
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,820
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,903