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


好吧,最近看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('***')/document.getElementByName('***')),如果通過xpath那么轉換的就不一定那么順利了,所以selenium1盡量少用xpath。說了怎么多,我們明白了selenium_Core其實是個js代碼庫,我們沒必要太關系,我們要做的是寫好客戶端代碼。

說完了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的傳值基本是相同的

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的啟動代碼:

 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)
 
        

看看紅色部分,其實3.0走的是if的語句,而2.0的marionette默認是False走的是else語句。

不管是走if語句還是else語句他們都干了2件事情,第一件:啟動本地RC。3.0體現在 C:\Python27\Lib\site-packages\selenium\webdriver\common\service.py中的:

 

 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中的

 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類我們看看這個類:

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,我們加上就好了

如下:

 

 RemoteWebDriver.__init__(
                self,
                command_executor=executor,
                browser_profile=self.profile,
                desired_capabilities=capabilities,
                keep_alive=True)

 

 寫了好久,我們話題一暫時說到這里!以后寫一篇關於selenium一些我們不常見但很實用的包或模塊的相關分析和使用,我們下個話題見。

 

 












 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM