selenium_webdriver_(python)第三版 - GitHubliuxianhui/python GitHub Wiki

博客园—虫师 http://1 fnng.cnblogs.com selenium webdriver (python) (第三版) 声明: 本文档以收费方式出售,未经过作者本人允许,禁止一切非法的传播,禁止用于任何商 业用途。 博客园--虫师 [email protected] 博客园—虫师 http://2 fnng.cnblogs.com 前言 对于大多软件测试人员来讲缺乏编程经验(指项目开发经验,大学的C 语言算很基础 的编程知识)一直是难以逾越的鸿沟,并不是说测试比开发人员智商低,是国内的大多测试 岗位是功能测试为主,在工作时间中,我们很难深入的接触和使用编程技术; 笔者认为自动化测试尽管有很多不足,更不能完全替代手工测试,但确实是测试人员发 展的一个方向,越来越多的公司在实践自动化,越来多的项目在尝试自动化;所以对于功能 测试人员来讲,掌握项目自动化测试技术自然能提高测试技术水平,能够保持不被淘汰,又 能在激烈的竞争中处于优势地位。 为什么选python,因为他语法简单;如果你有一点C 语言或java 语言基础的,将会非 常容易地学会并使用python。自动化脚本本身要比开发程序简单得多,大多人学编程半途而 废就是没有实践的机会;那么通过selenium webdriver python 进行自动化测试,很快就可以 学以致用,建立继续学习的信心与动力;可以平滑的过渡到真正的编程经验上。 如果要使用java 或ruby 语言通过selenium webdriver 来实施自动化测试,虽然各种语 言的语法有差别,但思路是相通的;相信本文档依然可以提供给你学习的思路。 继续在这里感谢: 感谢购买第二版的同学,谢谢你们对本人劳动成果的支持!也正是你们时常问我还出不 出第三版了,也是你们的鼓励,让我继续学习整理本文档。 感谢乙醇前辈,第二版的文档是放在他的淘宝网站上卖的,感谢他的帮忙。 最最感谢的还是兔子( MarkRabbit ),好吧!他已经极力抗议叫兔子了,哈哈!本文 档中相当多的知识点是他提供的,不过他只提供思路,不提供解决问题的具体代码;我需要 把他的话截图下来,反复理解,然后找具体的解决代码,因此,我python 的语言能力提高 了不少。 下面要简单说说本文档的内容: 《selenium webdriver python (第三版)》相比第二版增加测试套件,参数化问题,引入 HTMLTestRunner ,测试结构的调整,相对来说比较好的构建了测试结构(只能用是“结构”, 离“架构”还差得呢!) 本文档仍然有很多不足够,毕竟不是以出书的标准来要求的,可能很多知识点解释的不 透彻,甚至错误的地方,请提出你的意见给本人。 本文档不是API,所以还有很多方法没有整理,如果在学习的过程中有任何疑问,请查 阅在线AIP 文档: http://selenium.googlecode.com/git/docs/api/py/index.html 2013.10.31 博客园—虫师 http://3 fnng.cnblogs.com 目录 一、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 tag name 和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://4 fnng.cnblogs.com 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://5 fnng.cnblogs.com 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 博客园—虫师 http://6 fnng.cnblogs.com 一、selenium+python 环境搭建 1.1 selenium 介绍 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 下一样可用。 博客园—虫师 http://7 fnng.cnblogs.com 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.py install (如果提示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://8 fnng.cnblogs.com 七、下载并安装 (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

from selenium 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://9 fnng.cnblogs.com 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 穿上selenium webdriver 是多么的性感: 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://10 fnng.cnblogs.com 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://11 fnng.cnblogs.com 三、元素的定位 对象的定位应该是自动化测试的核心,要想操作一个对象,首先应该识别这个对象。 一个对象就是一个人一样,他会有各种的特征(属性),如比我们可以通过一个人的身 份证号,姓名,或者他住在哪个街道、楼层、门牌找到这个人。 那么一个对象也有类似的属性,我们可以通过这个属性找到这对象。 webdriver 提供了一系列的对象定位方法,常用的有以下几种 ? · id ? · name ? · class name ? · link text ? · partial link text ? · tag name ? · xpath ? · css selector 我们可以看到,一个百度的输入框,可以用这么用种方式去定位。 #coding=utf-8 from selenium 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://12 fnng.cnblogs.com #通过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 命名时一般使其有意义也会取不同的名字。通过这两个属性使我 们找一个页面上的属性变得相当容易 我们通过前端工具,找到了百度输入框的属性信息,如下: 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(标签名) 博客园—虫师 http://13 fnng.cnblogs.com 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 属性 新闻 driver.find_element_by_css_selector("a[name="tj_news"]").click() 可以取title 属性 网页 driver.find_element_by_css_selector("a[title="web"]").click() 也可以是取..: driver.find_element_by_css_selector("a.RecycleBin").click() 虽然我也没全部理解CSS 的定位,但是看上去应该是一种非常灵活和牛X 的定位方式 扩展阅读: http://www.w3.org/TR/css3-selectors/ 博客园—虫师 http://14 fnng.cnblogs.com 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://15 fnng.cnblogs.com 3.5 link 定位 有时候不是一个输入框也不是一个按钮,而是一个文字链接,我们可以通过link #coding=utf-8 from selenium import webdriver browser = webdriver.Firefox() browser.get("http://www.baidu.com") browser.find_element_by_link_text("贴吧").click() browser.quit() 一般一个页面上不会出现相同的文件链接,通过文字链接来定位也是一种简单有效 的定位方式。 3.6 Partial link text 定位 通过部分链接定位,这个有时候也会用到,我还没有想到很好的用处。拿上面的例 子,我可以只用链接的一部分文字进行匹配: browser.find_element_by_partial_link_text("贴").click() #通过find_element_by_partial_link_text() 函数,我只用了“贴”字,脚本一样找到了"贴吧 " 的链接 四、添加等待时间 有时候为了保证脚本运行的稳定性,需要脚本中添加等待时间。 4.1、添加休眠 添加休眠非常简单,我们需要引入time 包,就可以在脚本中自由的添加休眠时间了。

coding = utf-8

博客园—虫师 http://16 fnng.cnblogs.com from selenium 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

from selenium 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://17 fnng.cnblogs.com 五、打印信息 很多时间我们不可能盯着脚本执行,我们需要一些打印信息来证明脚本运行是否正确: 5.1、打印tile 把刚才访问页面的title 打印出来。 coding = utf-8 from selenium 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 from selenium import webdriver import time browser = webdriver.Firefox() url= 'http://www.baidu.com' 博客园—虫师 http://18 fnng.cnblogs.com #通过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 from selenium 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://19 fnng.cnblogs.com 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 from selenium import webdriver import time browser = webdriver.Firefox() #访问百度首页 first_url= 'http://www.baidu.com' 博客园—虫师 http://20 fnng.cnblogs.com 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 "forward to %s"%(second_url) browser.forward() time.sleep(2) browser.quit() 为了使过程让你看得更清晰,在每一步操作上都加了print 和sleep 。 说实话,这两个功能平时不太常用,所能想到的场景就是几个页面来回跳转,但又 不想用get url 的情况下。 八、操作测试对象 前面讲到了不少知识都是定位元素,定位只是第一步,定位之后需要对这个原素进 行操作。鼠标点击呢还是键盘输入,这要取决于我们定位的是按钮还输入框。 博客园—虫师 http://21 fnng.cnblogs.com 一般来说,webdriver 中比较常用的操作对象的方法有下面几个 ? click 点击对象 ? send_keys 在对象上模拟按键输入 ? clear 清除对象的内容,如果可以的话 ? submit 清除对象的内容,如果可以的话 ? text 用于获取元素的文本信息 8.1、鼠标点击与键盘输入 在我们本系列开篇的第一个例子里就用到了到click 和send_skys ,别翻回去找 了,我再贴一下代码: coding=utf-8 from selenium 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 from selenium import webdriver import time 博客园—虫师 http://22 fnng.cnblogs.com 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 from selenium 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://23 fnng.cnblogs.com 获得属性值。 这个函数的用法前面已经有出现过,在定位一组元素的时候有使用到它,只是我们 没有做过多的解释。 一般用法: 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 from selenium import webdriver from selenium.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://24 fnng.cnblogs.com 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 包: from selenium.webdriver.common.keys import Keys 通过send_keys()调用按键: send_keys(Keys.TAB) # TAB send_keys(Keys.ENTER) # 回车 注意:这个操作和页面元素的遍历顺序有关,假如当前定位在账号输入框,按键 盘的tab 键后遍历的不是密码框,那就不法输入密码。假如输入密码后,还有 需要填写验证码,那么回车也起不到登陆的效果。 9.2、键盘组合键用法 #coding=utf-8 from selenium import webdriver from selenium.webdriver.common.keys import Keys import time driver = webdriver.Firefox() driver.get("http://www.baidu.com") 博客园—虫师 http://25 fnng.cnblogs.com #输入框输入内容 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://26 fnng.cnblogs.com 那么鼠标的双击、右击、拖动等是否也是这样的写法呢?例如右击: driver.find_element_by_id(“xxx”).context_click() 经过运行脚本得到了下面的错误提示: AttributeError: 'WebElement' object has no attribute 'context_click' 提示右点方法不属于webelement 对象, 通过查找文档, 发现属于 ActionChains 类,但文档中没有具体写法。这里要感谢北京-QC-rabbit 的指 点,其实整个python+selenium 学习过程都要感谢北京-QC-rabbit 的指点。 10.1、鼠标右键 下面介绍鼠标右键的用法,以快播私有云为例: #coding=utf-8 from selenium import webdriver from selenium.webdriver.common.action_chains import 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://27 fnng.cnblogs.com ''' #你也可以使用三行的写法,但我觉得上面两行写法更容易理解 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://28 fnng.cnblogs.com #执行元素的移动操作 ActionChains(driver).drag_and_drop(element, target).perform() 十一、定位一组元素 webdriver 可以很方便的使用findElement 方法来定位某个特定的对象,不过有时 候我们却需要定位一组对象,这时候就需要使用findElements 方法。 定位一组对象一般用于以下场景: ? 批量操作对象,比如将页面上所有的checkbox 都勾上 ? 先获取一组对象,再在这组对象中过滤出需要具体定位的一些对象。比如定位出页 面上所有的checkbox,然后选择最后一个 checkbox.html

<title>Checkbox</title> <script type="text/javascript" async="" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"> script>

checkbox

checkbox1
博客园—虫师 http://29 fnng.cnblogs.com checkbox2
checkbox3
radio
radio
将这段代码保存复制到记事本中,将保存成checkbox.html 文件。(注意,这个页面需要 和我们的自动化脚本放在同一个目录下) 通过浏览器打开,得到下列页面: 博客园—虫师 http://30 fnng.cnblogs.com 11.1、第一种定位方法 通过浏览器打个这个页面我们看到三个复选框和两个单选框。下面我们就来定位这 三个复选框。 # -*- coding: utf-8 -*- from selenium 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 input in inputs: if input.get_attribute('type') == 'checkbox': input.click() time.sleep(2) dr.quit() import os 博客园—虫师 http://31 fnng.cnblogs.com 注意:因为我们调用的是本地文件, 所以要导入os 包。 11.2、第二种定位方法 第二种写法与第一种写法差别不大,都是通过一个循环来勾选控件。 # -*- coding: utf-8 -*- from selenium 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://32 fnng.cnblogs.com 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 <title>frame</title> 博客园—虫师 http://33 fnng.cnblogs.com <script type="text/javascript" async=""src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js "></script> <script type="text/javascript">$(document).ready(function(){ }); </script>

frame

<iframe id="f1" src="inner.html" width="800", height="600"></iframe>
<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap. min.js"></script> inner.html <title>inner</title>

inner

<iframe id="f2" src="http://www.baidu.com" width="700"height="500"></iframe> click
frame.html 中嵌套inner.html ,两个文件和我们的脚本文件放同一个目录下,通过 博客园—虫师 http://34 fnng.cnblogs.com 浏览器打开,得到下列页面: 下面通过switch_to_frame() 方法来进行定位: #coding=utf-8 from selenium 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://35 fnng.cnblogs.com 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 <title>Level Locate</title> <script type="text/javascript" async="" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"> 博客园—虫师 http://36 fnng.cnblogs.com

Level locate

<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min .js"></script> 上面的html 代码比较乱,请复制到编辑器中查看,如nodepad ++ 编辑器。 (注意,这个页面需要和我们的自动化脚本放在同一个目录下)通过浏览器打开: 博客园—虫师 http://37 fnng.cnblogs.com 定位思路: 具体思路是:先点击显示出1个下拉菜单,然后再定位到该下拉菜单所在的ul,再 定位这个ul 下的某个具体的link。在这里,我们定位第1个下拉菜单中的Action 这个 选项。 脚本如下: # -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.support.ui import 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://38 fnng.cnblogs.com 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://39 fnng.cnblogs.com 14.1、操作文件上传例子 upload_file.html <title>upload_file</title> <script type="text/javascript" async=""src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js "></script> <script type="text/javascript"> </script>

upload_file

<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap. min.js"></script> 通过浏览器打开,得到下列页面: 操作上传脚本: #coding=utf-8 from selenium import webdriver 博客园—虫师 http://40 fnng.cnblogs.com 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 from selenium 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://41 fnng.cnblogs.com 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 UPS Next Day Air ==> $12.51 UPS Next Day Air Saver ==> $11.61 UPS 3 Day Select ==> $10.69 UPS 2nd Day Air ==> $9.03 UPS Ground ==> $8.34 USPS Priority Mail Insured ==> $9.25 USPS Priority Mail ==> $7.45 USPS First Class ==> $3.20 博客园—虫师 http://42 fnng.cnblogs.com 保存并通过浏览器打开,如下: 现在我们来通过脚本选择下拉列表里的$10.69 #-*-coding=utf-8 from selenium 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://43 fnng.cnblogs.com 15.2、百度搜索设置下拉框操作 #-*-coding=utf-8 from selenium 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://44 fnng.cnblogs.com 焦点集中到页面上的一个警告(提示) – 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://45 fnng.cnblogs.com 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://46 fnng.cnblogs.com <title>modal</title> <script type="text/javascript" async="" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function(){ $('#click').click(function(){ $(this).parent().find('p').text('Click on the link to success!'); }); }); </script>

modal

Click
×

Modal header

Congratulations, you open the window!

click me
Close Save changes
博客园—虫师 http://47 fnng.cnblogs.com <title>modal</title> <script type="text/javascript" async="" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function(){ $('#click').click(function(){ $(this).parent().find('p').text('Click on the link to success!'); }); }); </script>

modal

Click
×

Modal header

Congratulations, you open the window!

click me
Close Save changes
博客园—虫师 http://48 fnng.cnblogs.com
<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script> 代码有点长,你可以直接赋值粘贴到Notepad++中,保存成html 通过浏览器打开,效 果如下: 操作脚本如下: # -*- coding: utf-8 -*- from selenium import webdriver from time import sleep import os import selenium.webdriver.support.ui as ui 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://49 fnng.cnblogs.com # 由于对话框中的元素被蒙板所遮挡,直接点击会报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://50 fnng.cnblogs.com #回到原先的窗口 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 <title>js</title> <script type="text/javascript" async="" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"> <script type="text/javascript"> $(document).ready(function(){ $('#tooltip').tooltip({"placement": "right"}); }); </script>

js

<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap. min.js"></script> (保持html 文件与执行脚本在同一目录下) 保存并通过浏览器打开,如下: 执行js 一般有两种场景: ? 一种是在页面上直接执行JS ? 另一种是在某个已经定位的元素上执行JS #coding=utf-8 from selenium import webdriver import time,os 博客园—虫师 http://52 fnng.cnblogs.com 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 from selenium 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://53 fnng.cnblogs.com 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 ,所以花了不小力气找到这种方法。 用于标识滚动条位置的代码 如果滚动条在最上方的话,scrollTop=0 ,那么要想使用滚动条在最可下方,可以 scrollTop=100000 ,这样就可以使滚动条在最下方。 博客园—虫师 http://54 fnng.cnblogs.com 19.1、场景一 先来解决场第一个问题,法律条款是一个内嵌窗口,通过firebug 工具可以定位到内嵌 入窗口可以定位到元素的id ,可以通过下面的代码实现。 js="var q=document.getElementById('id').scrollTop=10000" driver.execute_script(js) 19.2、场景二 有滚动条的页面到处可见,这个就比较容易找例子,我们以操作百度搜索结果页为例: #coding=utf-8 from selenium 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://55 fnng.cnblogs.com 二十、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 from selenium 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'[email protected]', 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://56 fnng.cnblogs.com u'abcUX9zdw0minadIhtvcu', u'secure': False}] 20.2、对cookie 操作 上面的方式打印了所有cookie 信息,太多太乱,我们只想有真对性的打印自己想要 的信息,看下面的例子 #coding=utf-8 from selenium 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 -> [email protected] JSESSIONID -> abc7qSE_SBGsVgnVLBvcu key-aaaaaaa -> value-bbbb # 这一条是我们自己添加的 博客园—虫师 http://57 fnng.cnblogs.com 20.3、博客园登陆分析cookie 通过博客园登陆来分析cookie #coding=utf-8 from selenium 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://58 fnng.cnblogs.com 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://59 fnng.cnblogs.com #coding = utf-8 import time from selenium 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 -jar selenium-server-standalone-2.33.0.jar 八月22, 2013 10:19:48 上午org.openqa.grid.selenium.GridLauncher main INFO: Launching a standalone server 10:19:48.734 INFO - Java: Oracle Corporation 23.21-b01 10:19:48.734 INFO - OS: Windows XP 5.1 x86 10:19:48.734 INFO - v2.33.0, with Core v2.33.0. Built from revision 4e90c97 10:19:48.843 INFO - RemoteWebDriver instances should connect to: http://127.0.0. 1:4444/wd/hub 10:19:48.843 INFO - Version Jetty/5.1.x 10:19:48.843 INFO - Started HttpContext[/selenium-server/driver,/selenium-server /driver] 博客园—虫师 http://60 fnng.cnblogs.com 10:19:48.843 INFO - Started HttpContext[/selenium-server,/selenium-server] 10:19:48.843 INFO - Started HttpContext[/,/] 10:19:48.890 INFO - Started org.openqa.jetty.jetty.servlet.ServletHandler@176343e 10:19:48.890 INFO - Started HttpContext[/wd,/wd] 10:19:48.906 INFO - Started SocketListener on 0.0.0.0:4444 10:19:48.906 INFO - Started org.openqa.jetty.jetty.Server@388c74 -------------------------------------------------------------------------------------- 创建新session 10:20:38.593 INFO - Executing: [new session: {platform=ANY, javascriptEnabled=tr ue, browserName=chrome, version=}] at URL: /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]: received Webriver request: GET /status 向webdrver 返回响应,返回码200表示成功 [0.921][INFO]: sending Webriver response: 200 { "sessionId": "", "status": 0, "value": { "build": { "version": "alpha" }, "os": { "arch": "x86", "name": "Windows NT", "version": "5.1 SP3" } } } webdriver 再次以POST 方式发送请求,并启动浏览器相关信息 [0.984][INFO]: received Webriver request: POST /session { "desiredCapabilities": { "browserName": "chrome", "javascriptEnabled": true, "platform": "ANY", "version": "" } 博客园—虫师 http://61 fnng.cnblogs.com } [0.984][INFO]: Launching chrome: "C:\ocuments and Settings\Administrator\Local S 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 s data:text/html;charset=utf-8, [1.773][INFO]: sending Webriver response: 303 webdriver 再次以GET 方法请求,这附加上了session 的信息 [1.778][INFO]: received Webriver request: GET /session/32b33aa585ccbbf7ba7853588 2852af3 服务器先对sesssionID 进行解析,确认是selenium 调用的以及要访问的网址, [1.779][INFO]: sending Webriver response: 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://62 fnng.cnblogs.com 10:20:40.640 INFO - Executing: org.openqa.selenium.remote.server.handler.GetSess ionCapabilities@14cf7a1 at URL: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc) 10:20:40.640 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc 10:20:40.656 INFO - Executing: [get: http://www.youdao.com] at URL: /session/ac5 b2c71-5b1a-469e-814c-fdd09a2061fc/url) webdriver 正试向服务器请求youdao 网站 [1.820][INFO]: received Webriver request: POST /session/32b33aa585ccbbf7ba785358 82852af3/url { "url": "http://www.youdao.com"} [1.822][INFO]: waiting for pending navigations... [1.829][INFO]: done waiting for pending navigations [2.073][INFO]: waiting for pending navigations... [2.900][INFO]: done waiting for pending navigations 获得服务器数据的应答 [2.900][INFO]: sending Webriver response: 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]: received Webriver request: POST /session/32b33aa585ccbbf7ba785358 82852af3/element { "using": "name", "value": "q"} [2.905][INFO]: waiting for pending navigations... [2.905][INFO]: done waiting for pending navigations [2.922][INFO]: waiting for pending navigations... [2.922][INFO]: done waiting for pending navigations 得到服务器应答 [2.922][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", 博客园—虫师 http://63 fnng.cnblogs.com "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]: received Webriver 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]: done waiting for pending navigations [3.002][INFO]: waiting for pending navigations... [3.002][INFO]: done waiting for pending navigations [3.002][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": null} 10:20:41.843 INFO - 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]: received Webriver request: POST /session/32b33aa585ccbbf7ba785358 82852af3/element { "using": "name", "value": "q"} [3.006][INFO]: waiting for pending navigations... [3.006][INFO]: done waiting for pending navigations [3.016][INFO]: waiting for pending navigations... 博客园—虫师 http://64 fnng.cnblogs.com [3.016][INFO]: done waiting for pending navigations [3.016][INFO]: sending Webriver response: 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]: received Webriver 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]: done waiting for pending navigations [3.064][INFO]: waiting for pending navigations... [3.064][INFO]: done waiting for pending navigations [3.064][INFO]: sending Webriver response: 200 { "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": null} 10:20:41.906 INFO - 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]: received Webriver request: ELETE /session/32b33aa585ccbbf7ba78535 882852af3/window [WARNING:chrome_desktop_impl.cc(88)] chrome detaches, user should take care of d irectory:C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\scoped_dir1808_7550 and C:\DOCUME~1\ ADMINI~1\LOCALS~1\Temp\scoped_dir1808_26821 [5.318][INFO]: sending Webriver response: 200 { 博客园—虫师 http://65 fnng.cnblogs.com "sessionId": "32b33aa585ccbbf7ba78535882852af3", "status": 0, "value": null} 10:20:44.156 INFO - Done: /session/ac5b2c71-5b1a-469e-814c-fdd09a2061fc/window 第二部分:框架的力量 博客园—虫师 http://66 fnng.cnblogs.com 二十二、引入unittest 框架 unittest 框架学习 借助IED 录制脚本, 博客园—虫师 http://67 fnng.cnblogs.com 将脚本导出,保存为baidu.py ,通过python IDLE 编辑器打开。如下: from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import 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(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://68 fnng.cnblogs.com 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://69 fnng.cnblogs.com 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://70 fnng.cnblogs.com 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 关闭警告和对得到文本框的处理,如果不熟悉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://71 fnng.cnblogs.com 很帅吧!? 后面将以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): if width < 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://72 fnng.cnblogs.com 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 看成是包含所有测试用例的一个容 器。 框架分析 软件测试中最基本的组成是单元测试用例(test case),我们在实际测试过程中,不 可能真对一个功能(类)只写一个用例。TestCase 在PyUnit 测试框架中被视为测试单 元的运行实体,Python 程序员可以通过它派生自定义的测试过程与方法(测试单元),利 博客园—虫师 http://73 fnng.cnblogs.com 用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://74 fnng.cnblogs.com 的入口,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://75 fnng.cnblogs.com 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://76 fnng.cnblogs.com 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.ui import 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://77 fnng.cnblogs.com #进入搜索设置页 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://78 fnng.cnblogs.com 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] #选取所要执行的用例 if s=='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://79 fnng.cnblogs.com 截图保存,这将是一个非常棒的功能,也会给我们错误定位带来方便。 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.ui import 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://80 fnng.cnblogs.com 异常进行捕捉;并把结果保存下来。再次执行你的脚本会发现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://81 fnng.cnblogs.com 二十六、生成测试报告(HTMLTestRunner) 在脚本云行完成之后,除了在log.txt 文件看到运行日志外,我们更希望能生一张漂亮 的测试报告来展示用例执行的结果。 下面我们就通过HTMLTestRunner.py 来生成测试报告。 首先要下HTMLTestRunner.py 文件,下载地址: http://tungwaiyip.info/software/HTMLTestRunner.html 将下载的文件放入...\Python27\Lib 目录下(windows),打开交互模式引入包,如果没有 报错,说明添加成功,当然也可以通过dir() 看看'HTMLTestRunner 包含发哪些方法。 >>> import HTMLTestRunner >>> 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.ui import Select from selenium.common.exceptions import NoSuchElementException import unittest, time, re import HTMLTestRunner class Baidu(unittest.TestCase): 博客园—虫师 http://82 fnng.cnblogs.com 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://83 fnng.cnblogs.com 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://84 fnng.cnblogs.com 下面的也很容易理解,创建result.html 文件,给以读写权限(wb),调用HTMLTestRunner 文件,并将测试结果以HTMLTestRunner 规定的格式通过fp 传递写入到result.html 文件中。 最后是运行的testunit ,也就是TestSuite 中的所有用例。 脚本运行结束,生成如下报告: 问题: 这个报告是根据一个.py 文件生成的,这样就迫使我们把所有用例都写在一个.py 文件 里,如果我们每一个用例都写在不同的.py 文件里将生成很多个报告,不便于阅读;但写在 一个.py 文件里,如果用例非常多的话,同样不便于维护。 后面,我们将一起寻求解决办法。 二十七、数据驱动测试 先来理解一下自动化领域的两种驱动,对象驱动与数据驱动。 数据驱动:测试数据的改变引起执行结果的改变叫数据驱动; 关键字驱动:测试对象名字的改变起引起测试结果的改变叫关键字驱动。 27.1、读取文件参数化 博客园—虫师 http://85 fnng.cnblogs.com 以百度表搜索为例,我们可以通过脚本循环执行,读取一文件中不同的内容来完成自动 化工作,也就是说我们每次取的文件里的搜索关键字不同,而每次百度搜索的的结果不同, 这也是数据驱动的本质。 代码如下: 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://86 fnng.cnblogs.com 将数据传入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://87 fnng.cnblogs.com 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 username and password!!" return d 字典的可以方便的存放k,v 键值对,一个键对应一个值;注意,如果密码中有非数字, 需要加单引号。 下面循环调用词典的值: #coding=utf-8 from selenium import webdriver import os,time import fun #导入函数 博客园—虫师 http://88 fnng.cnblogs.com #循环调用字典里的用户名密码,分别赋值给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://89 fnng.cnblogs.com 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://90 fnng.cnblogs.com 如果学好了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://91 fnng.cnblogs.com from selenium.webdriver.support.ui import 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://92 fnng.cnblogs.com 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.ui import 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://93 fnng.cnblogs.com #百度搜索用例 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://94 fnng.cnblogs.com #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://95 fnng.cnblogs.com #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://96 fnng.cnblogs.com 28.3、更易读的报告 报告已经接近完美了,唯一的一点小瑕疵,这报告如果给领导看的话,哪知道什么是什 么,经过MarkRabbit 的指点,我们可以给每一个用例加个中文注释。 ............ #百度搜索用例 def test_baidu_search(self): u"""百度搜索用例""" driver = self.driver driver.get(self.base_url + "/") ................ 每个用例(方法)下面都可以加这个一行注释信息,小u 是避免中文引起的乱码问题。 博客园—虫师 http://97 fnng.cnblogs.com 再来跑一下用例,找开生成的报告,是不是完美了,傻瓜都知道是干嘛的。 二十九、结构改进 到目前为止问题已经解决了,通过测试套件执行所有用例,测试报告整合,所有文件都 在一个目录下面,估计用例写多了,不方便管理,这一章试着调整一下结构。 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://98 fnng.cnblogs.com 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://99 fnng.cnblogs.com 其实__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://100 fnng.cnblogs.com 文件,是不是还可以导入其它目录下包,或者模块。假设在/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://101 fnng.cnblogs.com #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(), # 但如果不提供一个良好的错误消息时,它无法加载测试 # 所以我们加载所有单独的测试,这样将会提高脚本错误的确定。 for test in alltestnames: try: #最关键的就是这一句,循环执行数据数的里的用例。 suite.addTest(unittest.defaultTestLoader.loadTestsFromName(test)) except Exception: print 'ERROR: Skipping tests from "%s".' % test try: __import__(test) except ImportError: print 'Could not import the test 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://102 fnng.cnblogs.com 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://103 fnng.cnblogs.com return alltestnames 在all_tests.py 中进行调用: #coding=utf-8 .... import allcase_list #调用数组文件 #获取数组方法 alltestnames = allcase_list.caselist() ... suite = unittest.TestSuite() if __name__ == '__main__': for test in alltestnames: 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://104 fnng.cnblogs.com 目前看上去还不错的样子~!(得意笑),但是我们项目不同,需求不同,或者当用例达 到一定量级后,还会有很多问题暴露出来,需要我们一一的去解决;好吧~!第三版的内容 就到这里了。 三十、UliPad--python 开发利器 工欲善其事,必先利其器 有时候往往选择太多,变得无从选择。如果你在python 开发中已经找到了趁手的IDE 这 一节可以无视。 其实,pyhon 下面能找到一款不错的开发工具是不太容易的。 IDLE 写写单个小程序很好,但一个程序文件与执行信息是两个窗口,程序开多了就分 不清哪个了。 pythonWin 也用过,窗口有些老土,窗口布局我不会设置,所以觉得也不好用。 notepad++ 这种小巧的万能编辑器,偶尔用用还行。 linux 会有一些非常不错的交互式python IDE ,如ipython 、bpython 等。 vim 肯定是开发神器,但一般也只有高手才会运用自如,体会它的奥妙。 UliPad 是找到的写python 最舒服的一个IDE 。 地址:https://code.google.com/p/ulipad/ 免费,可以免费获得并使用它的所有功能。 支持windows 、MAC、linux 等平台。 小巧,内存占用很少,10MB 左右。 博客园—虫师 http://105 fnng.cnblogs.com 具体,的安装使用,这里就不介绍了,不是本文档的主题。有兴趣使用可以参考我的博客: http://www.cnblogs.com/fnng/p/3393275.html 另外,还有一些非常棒的收费python IDE Wing IDE4.1 http://wingware.com/ pycharm http://www.jetbrains.com/pycharm/ 希望这一节没影响到文档的和谐。呵呵~! 博客园—虫师 http://106 fnng.cnblogs.com 后记: 都在谈自动化测试,自动化测试是“部分”功能测试的一种替代技术(它们比例肯定在 逆转)。通过学习自动脚本也可以使测试人员突破不懂代码的限制;而自动化脚本入门简单。 我觉得自动化是方向。 关于自动化又帮了你一段路,但是,依然还有很多问题没有解决;比如,测试用例的多 线程处理。目前的结构还不够完美,在脚本运行中,我们可以捕捉更多的信息,更容易的定 位问题;使我们的结构更灵活的适应需求的变化;路还很长,任重道远,一起加油吧! 这些问题依然不是一份学习文档可以解决的,如果你掌握了本文档的所有内容,建议从 以下几个方面来提高自己的自动化测试水平: python 语言:兔子(它不让叫兔子了,叫MarkRabbit )的话清晰的说明了学习自动 化测试的思路:我们需要用python 这门语言去调用selenium 的一些工具来操作浏览器, 帮助我们实现“web UI ”自动化。所以,我们的重心应该放在语言本身的学习。后面这几 章解决问题用的也是python 技术。 Javascript 语言:在实际的自动化测试过程中,我们会遇到各种问题,有时候webdriver 提供的方法不能帮我们解决问题,那么需要借助Javascript 来解决问题。 xpath \css 定位: 不能操作一个元素,很多情况下是我们没办法定位这个元素;所以要 深入了解xpath \css 定位的用法。 扩展资料: rt sm 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://107 fnng.cnblogs.com 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。 大家可以了解一下webdriver guide 的内容 webdriver API 地址: 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 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 博客园—虫师 http://108 fnng.cnblogs.com http://www.cnblogs.com/ziyunfei/archive/2012/09/28/2706061.html__
⚠️ **GitHub.com Fallback** ⚠️