selenium相關技術研究(從1.0-3.0)

注: 如下內容引自http://www.cnblogs.com/hhudaqiang/p/6550135.htmljavascript

好吧,最近看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('***')/document.getElementByName('***')),若是經過xpath那麼轉換的就不必定那麼順利了,因此selenium1儘可能少用xpath。說了怎麼多,咱們明白了selenium_Core實際上是個js代碼庫,咱們不必太關係,咱們要作的是寫好客戶端代碼。node

說完了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>
相關文章
相關標籤/搜索