好吧,最近看wxpython有点多。鉴于最近selenium3.0的出现,有些同事更新了selenium发现若干的坑(包括若干bug)。selenium能够说是自动化测试框架的核心,不论是robotframework这样成熟的测试框架,仍是本身写的架构都离不开这个包;无论你是web测试仍是app的测试你也离开不了它。关于这个包太多要研究的,这里我大体讲诉下个人一些可能不太正确的见解,无论对本身对别人都是一种记录吧~~css
话题1、selenium发展历程html
说到发展历程,有些朋友多是不屑于了解与研究的,我以为你既然用这个东西,不论是“道义”上仍是深刻理解发面你应该对他的成长有所了解。selenium目前最新的版本是selenium3.0,咱们看看每一个阶段selenium是什么样的,该怎么使用它?
前端
1.使用selenium1实际上是比较麻烦的,就是必需要启用selenium-server-standalone-x.xx.x.jar这个包,为何启用看完这个老大的架构就明白了。简单点说selenium1=selenium_Core+selenium_Rc。java
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代码库,咱们不必太关系,咱们要作的是写好客户端代码。nodedocument.getElementByName(
'***'
)
说完了selenium_Core,咱们来谈谈selenium_Rc,其实这个就是个服务器,底层就是启用个socket,接受客户端传递过来的数据,用过Appium的同窗应该知道,在运行客户端代码以前咱们要启动Appium服务器,这东西其实和这个Rc大同小异,也是监听客户端传递过来的数据。不过Appium是将代码转成Andriod/IOS底层能执行的代码,看源码其实知道对于Andriod来讲其实Appium底层仍是基于Robotium框架来操做手机的。python
因此对于selenium1.0来讲大概的使用方法以下:git
1.java -jar selenium-server-standalone-x.xx.x.jar#启动服务器 (固然包括一些参数 好比-port 等这里再也不赘述)github
2.browser=webdriver.Remote(command_executor='http://127.0.0.1:4444/wd/hub')(一样初始化有一些有用的参数,后面提到)web
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>
|