selenium相关技术研究(从1.0-3.0)
注: 以下内容引自http://www.cnblogs.com/hhudaqiang/p/6550135.html
好吧,最近看wxpython有点多。鉴于最近selenium3.0的出现,有些同事更新了selenium发现若干的坑(包括若干bug)。selenium可以说是自动化测试框架的核心,不管是robotframework这样成熟的测试框架,还是自己写的架构都离不开这个包;不管你是web测试还是app的测试你也离开不了它。关于这个包太多要研究的,这里我大致讲诉下我的一些可能不太正确的看法,不管对自己对别人都是一种记录吧~~
话题一、selenium发展历程
说到发展历程,有些朋友可能是不屑于了解与研究的,我觉得你既然用这个东西,不管是“道义”上还是深入理解发面你应该对他的成长有所了解。selenium目前最新的版本是selenium3.0,我们看看每个阶段selenium是什么样的,该怎么使用它?
1.使用selenium1其实是比较麻烦的,就是必须要启用selenium-server-standalone-x.xx.x.jar这个包,为什么启用看完这个老大的架构就明白了。简单点说selenium1=selenium_Core+selenium_Rc。
selenium_Core是个JS的代码库,当你访问一个页面是,比如 browser.get("http://baidu.com")这个前段的操作通过wire protocol协议传递给服务器端,服务器通过request接口访问"http://www.baidu.com",拿到response后将结果同步到浏览器上,同时这个时候将这一组js代码库(selenium_Core)同步加载到浏览器上,这样神不知鬼不觉的欺骗了你。当你点击一个元素的时候比如:element.click(),首先前端将这个点击转换成json格式的字符串,然后通过wire protocl协议传递给服务器(wire protocl协议没什么特殊的基于http协议,不过他传递的参数是json格式的,其实http协议也能传递json串),服务器拿到这个json串后解析,发现是个click那么,在js库里寻找类似方法得知这是个点击那么excute js代码:element.click() !那么如果js库里面找不到我们想要的操作怎么办?那当然报错了...所以建议我们在寻找元素的时候尽量通过id,其次css 这些代码很简单转换成js直接执行(document.getElementById(
'***'
)/
),如果通过xpath那么转换的就不一定那么顺利了,所以selenium1尽量少用xpath。说了怎么多,我们明白了selenium_Core其实是个js代码库,我们没必要太关系,我们要做的是写好客户端代码。document.getElementByName(
'***'
)
说完了selenium_Core,我们来谈谈selenium_Rc,其实这个就是个服务器,底层就是启用个socket,接受客户端传递过来的数据,用过Appium的同学应该知道,在运行客户端代码之前我们要启动Appium服务器,这东西其实和这个Rc大同小异,也是监听客户端传递过来的数据。不过Appium是将代码转成Andriod/IOS底层能执行的代码,看源码其实知道对于Andriod来说其实Appium底层还是基于Robotium框架来操作手机的。
所以对于selenium1.0来说大概的使用方法如下:
1.java -jar selenium-server-standalone-x.xx.x.jar#启动服务器 (当然包括一些参数 比如-port 等这里不再赘述)
2.browser=webdriver.Remote(command_executor='http://127.0.0.1:4444/wd/hub')(同样初始化有一些有用的参数,后面提到)
2.slenium2这个老二的出现极大的推进了selenium这个包在自动化测试中的地位,首先,它是兼容selenium1的。对于本地测试,如果你非要按1.0那样启动浏览器也是可以的,当然如果你不嫌弃麻烦。其次,webdriver是不依赖于js驱动的,无需注入selenium_Core,他是直接调用浏览器的原生态接口驱动。所有我们不需要启用RC了,只要本地启动一个服务器(准确的说是,浏览器的插件启动一个socket服务),接受客户端传递过来的json数据,接受过来直接通过浏览器原生接口操作浏览器。由于每个浏览器的原生接口不一定相同,所以我们注意到在seleium的webdirver下有各种浏览器的webdriver firefox/chrome/ie 。最后,既然我们用不到RC了是不是说明他没什么用呢,答案是否定的。
问题是这样的:现在有一个例子我们需要对不同的版本的firefox进行测试或者将服务器代码分布到其他电脑上怎么,该怎么办?
那第一个问题来说吧,我们知道一台电脑只能安装一种版本的firefox,我要同时自动化测试22.0和33.0怎么办。这就用到了集群的概念,服务器端注册hub
java -jar selenium-server-standalone-x.xx.x.jar -role hub
其他不同的电脑注册为node
java -jar selenium-server-standalone-x.xx.x.jar -role node -port ×××
我们启动2个进程在unnitest里面我们这样写:
#coding=utf-8
import multiprocessing
import unittest
from selenium import webdriver
class Firefox_test(unittest.TestCase):
def setUp(self):
FIREFOX = {
"browserName": "firefox",
"version": "",
"platform": "ANY",
"javascriptEnabled": True,
"marionette": False,
}
if multiprocessing.current_process().name=="22.0":
# 如果当前的进程是22.0,
FIREFOX["version"]="22.0"
else:
FIREFOX["version"] = "33.0"
self.browser=webdriver.Remote(command_executor='http://127.0.0.1:4444/wd/hub',desired_capabilities=FIREFOX)
def test1(self):
#do something
pass
def test2(self):
# do something
pass
上面什么意思呢,我们测试22.0和33.0 2个版本的firefox,我们启动2个进程,进程名称为"22.0"和"33.0"代表不同的版本号,当执行到Firefox_test种的setUp时,我们判断当前进程名称如果是22.0我们给desired_capabilities种的version赋值为"22.0",else为"33.0",这些都没有问题!那么问题来了当我们调用webdriver.Remote(command_executor='http://127.0.0.1:4444/wd/hub',desired_capabilities=FIREFOX)时,服务器会根据我们传递的desired_capabilities中浏览器版本的不同启动自己子node相应版本的浏览器吗?答案是肯定的。我们可以看出集群的意义,当然有些同学说我这测试2个版本的浏览器用2太机器 那我测试10个版本的firefox怎么办真的要买10台电脑~好消息是不需要!这里有个docker的概念,docker不是我们讨论的范畴我大致提一下。其实,这玩意就是个类虚拟机 。它就是个集装箱 每个集装箱是由不同的镜像来启动的。那么我们可以启动10个这样的集装箱 每个集装箱上装不同版本的浏览器问题不就解决了吗?关于相关的技术我贴个链接你们自己去看吧,我们不讨论了。链接:http://blog.csdn.net/wywin100/article/details/52198099?locationNum=7
还有好多可能没讨论了就这样写过了吧,我们来看看3.0
3.selenium3.0其实和2.0在一个地方有所改动就是在启动浏览器的构造函数上,因为如此可能从2.0升级到3.0可能启动浏览器报错。
说到构造函数我们先看看传值。3.0和2.0的传值基本是相同的
1
2
3
4
5
6
7
8
9
10
11
|
class WebDriver(RemoteWebDriver): # There is no native event support on Mac NATIVE_EVENTS_ALLOWED = sys.platform ! = "darwin" _web_element_cls = FirefoxWebElement def __init__( self , firefox_profile = None , firefox_binary = None , timeout = 30 , capabilities = None , proxy = None , executable_path = "geckodriver" , firefox_options = None , log_path = "geckodriver.log" ): |
firefox_profile:浏览器的插件,这个在自动化测试一些支付系统上一定要加上插件的路径,于2.0不同3.0会判断 firefox_profile是一个字符串还是FirefoxBinary这个类的实例所以传路径和值都没有问题,2.0一定要传FirefoxBinary的实例
firefox_binary:firefox的路径,这个其实只有在C:\Python27\Lib\site-packages\selenium\webdriver\common\desired_capabilities.py下的类变量FIREFOX这个字典marionette为False才有效。在3.0这个值默认是True的,所以造成启动浏览器有问题,等下我们会提到,在2.0下这个值是False
FIREFOX = {
"browserName": "firefox",
"version": "",
"platform": "ANY",
"javascriptEnabled": True,
"marionette": False,
}
timeout:客户端与浏览器所启动的socket服务器,最大连接时间,超时报错!
capabilities:上文已经提过就是desired_capabilities,可以自己增加描述。
proxy:代理设置
executable_path:这个是3.0与2.0的区别,也是3.0无法启动的主要问题
firefox_options:Options实例主要定义浏览器启动路径和插件路径,会覆盖capabilities中的"binary"值(如果有的话),最后会被上面提到的firefox_binary与firefox_profile覆盖(如果不为空)
下面说说3.0的启动问题,刚才上文提到FIREFOX这个字典marionette默认是True的我们看下,3.0的启动代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
if capabilities.get( "marionette" ): self .service = Service(executable_path, log_path = log_path) self .service.start() capabilities.update(firefox_options.to_capabilities()) executor = FirefoxRemoteConnection( remote_server_addr = self .service.service_url) RemoteWebDriver.__init__( self , command_executor = executor, browser_profile = self .profile, desired_capabilities = capabilities, keep_alive = True ) # Selenium remote else : if self .binary is None : self .binary = FirefoxBinary() if self .profile is None : self .profile = FirefoxProfile() # disable native events if globally disabled self .profile.native_events_enabled = ( self .NATIVE_EVENTS_ALLOWED and self .profile.native_events_enabled) if proxy is not None : proxy.add_to_capabilities(capabilities) executor = ExtensionConnection( "127.0.0.1" , self .profile, self .binary, timeout) RemoteWebDriver.__init__( self , command_executor = executor, desired_capabilities = capabilities, keep_alive = True ) |
1
|
|
看看红色部分,其实3.0走的是if的语句,而2.0的marionette默认是False走的是else语句。
不管是走if语句还是else语句他们都干了2件事情,第一件:启动本地RC。3.0体现在 C:\Python27\Lib\site-packages\selenium\webdriver\common\service.py中的:
1
2
3
|
self .process = subprocess.Popen(cmd, env = self .env, close_fds = platform.system() ! = 'Windows' , stdout = self .log_file, stderr = self .log_file) |
2.0体现在C:\Python27\Lib\site-packages\selenium\webdriver\firefox\extension_connection.py中的
1
|
self .binary.launch_browser( self .profile, timeout = timeout) |
不同的是:2.0是通过浏览器插件启动socket且启动了firefox浏览器,而3.0通过geckodriver启动本地socket服务器,而启动浏览器器的操作放在Webdriver的start_session方法中。
第二件事情:实例化一个远程连接类,不管是3.0的FirefoxRemoteConnection还是2.0的ExtensionConnection类其实都是继承于RemoteConnection类我们看看这个类:
1
2
3
4
5
|
class RemoteConnection( object ): """A connection with the Remote WebDriver server. Communicates with the server using the WebDriver wire protocol: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol""" |
上面说的很清除 了是一个基于wire protocol的Remote WebDriver server。
我们重点来看看启动问题,上文也说到了3.0启动浏览器的流程,我们知道它最终要在控制台里运行geckodriver,这个我们本地是没有的,是个firefox驱动我们下载完成后放在环境变量里就启动没问题了。比如我的Path加入"D:\geckodriver.exe",其实如果你想暴力点,直接修改FIREFOX这个字典marionette为False,这样走的和2.0一样的逻辑了,说到这其实3.0关于 firefox_profile是有bug的,我们构造函数里明明传递了路径,但启动的还是没有插件的浏览器。原因是没有传递这个变量给RemoteWebDriver,我们加上就好了
如下:
1
2
3
4
5
6
|
RemoteWebDriver.__init__( self , command_executor = executor, browser_profile = self .profile, desired_capabilities = capabilities, keep_alive = True ) |
写了好久,我们话题一暂时说到这里!以后写一篇关于selenium一些我们不常见但很实用的包或模块的相关分析和使用,我们下个话题见。
1
|
<br><br> |
1
|
<br><br> |
1
|
<span style = "color: #000000" ><br><br><br>< / span> |
1
|
<br><br><br><br> |
selenium相关技术研究(从1.0-3.0)的更多相关文章
- .NET Core技术研究系列-索引篇
随着.NET Core相关技术研究的深入,现在将这一系列的文章,整理到一个索引页中,方便大家翻阅查找,同时,后续也会不断补充进来. .NET Core技术研究-WebApi迁移ASP.NET Core ...
- [转帖]我最近研究了hive的相关技术,有点心得,这里和大家分享下。
我最近研究了hive的相关技术,有点心得,这里和大家分享下. https://www.cnblogs.com/sharpxiajun/archive/2013/06/02/3114180.html 首 ...
- 关于Web开发里并发、同步、异步以及事件驱动编程的相关技术
一.开篇语 我的上篇文章<关于如何提供Web服务端并发效率的异步编程技术>又成为了博客园里“编辑推荐”的文章,这是对我写博客很大的鼓励,也许是被推荐的原因很多童鞋在这篇文章里发表了评论,有 ...
- iOS开发——技术精华Swift篇&Swift 2.0和Objective-C2.0混编之第三方框架的使用
swift 语言是苹果公司在2014年的WWDC大会上发布的全新的编程语言.Swift语言继承了C语言以及Objective-C的特性,且克服了C语言的兼容性问题.Swift语言采用安全编程模式,且引 ...
- SAAS相关技术要点
这篇文章本来是我们开发组内部用的一个小文档.因为我们公司以前没有做SAAS的经验,就成立了一个小组做一做这方面的技术前探,我是成员之一.这篇文档想从宏观的层面把开发一个SAAS应用所要用到的技术点稍微 ...
- 重复数据删除(De-duplication)技术研究(SourceForge上发布dedup util)
dedup util是一款开源的轻量级文件打包工具,它基于块级的重复数据删除技术,可以有效缩减数据容量,节省用户存储空间.目前已经在Sourceforge上创建项目,并且源码正在不断更新中.该工具生成 ...
- 【转】手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)
1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理! 我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...
- 手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)
1.引言 特别说明:本文内容仅用于即时通讯技术研究和学习之用,请勿用于非法用途.如本文内容有不妥之处,请联系JackJiang进行处理! 我司有关部门为了获取黑产群的动态,有同事潜伏在大量的黑产群 ...
- Nginx技术研究系列6-配置详解
前两篇文章介绍了Nginx反向代理和动态路由: Ngnix技术研究系列1-通过应用场景看Nginx的反向代理 Ngnix技术研究系列2-基于Redis实现动态路由 随着研究的深入,很重要的一点就是了解 ...
随机推荐
- Android 客户端与服务器交互
在android中有时候我们不需要用到本机的SQLite数据库提供数据,更多的时候是从网络上获取数据,那么Android怎么从服务器端获取数据呢?有很多种,归纳起来有 一:基于Http协议获取数据方法 ...
- Redis客户端ServiceStack.Redis的简单使用
在nuget中下载ServiceStack.Redis,但是运行之后会出现一个问题: Exception: "Com.JinYiWei.Cache.RedisHelper"的类型初 ...
- Implement int sqrt(int x).
自己设计函数,实现求根号.x是非负整数. Input: 8 Output: 2 当开出根号后有小数,则省略小数部分.. 思路:只要找到一个数a,a*a<=x而且(a+1)*(a+1)>x, ...
- java web(1)
获取项目的根路径:this.getservletcontext().getRealPath() 下载:不正规做法:test/html!!!! 正规做法:1,在响应头设置:res.addHeader(& ...
- 2.3MySQL 自带工具使用介绍
1.mysql 首先看看“-e, --execute=name”参数,这个参数是告诉mysql,我只要执行“-e”后面的某个命令,而不是要通过mysql 连接登录到MySQL Server 上面.此参 ...
- SQL解决"双重职位的查询"
双重身份问题: create table role_tab ( person char(5) not null, role char(1) not null ) insert into role_t ...
- Day9 进程理论 开启进程的两种方式 多进程实现并发套接字 join方法 Process对象的其他属性或者方法 守护进程 操作系统介绍
操作系统简介(转自林海峰老师博客介绍) #一 操作系统的作用: 1:隐藏丑陋复杂的硬件接口,提供良好的抽象接口 2:管理.调度进程,并且将多个进程对硬件的竞争变得有序 #二 多道技术: 1.产生背景: ...
- Angular5的new feature
https://blog.angular.io/version-5-0-0-of-angular-now-available-37e414935ced Version 5.0.0 of Angular ...
- Tornado、Bottle以及Flask
最近接手一个Tornado项目代码,项目要在原有基础上做很大扩展,为了更好地吃透并扩展好这个项目,就对Tornado以及比较轻型的Bottle.Flask这些框架一一作了调研.其实若干年前做第一个Py ...
- 关于H5的Canvas
1.什么是canvas? <canvas>标签是h5新增的,通过脚本(通常是js)来绘制图形,canvas只是一个图形容器,或者说是画布. canvas可以绘制路径.图形.字以及添加图像. ...