源碼解讀:webdriver client的原理 (面試自動化:若是你認爲知道18種定位方式就算會自動化,那就太low了)

前言

又到年末了,羣裏不少朋友說要開始備戰2020金三銀四,其實,我建議是,若是你不是技術大牛,就不要去湊熱鬧。javascript

其實,如今(11,12月份)就是最佳換工做的時候,由於不少人想等着拿了年終再走,雖然招聘的公司很少,可是找工做的更少,因此,能夠去試試有沒有好機會。java

不少人說要去面試自動化,問我有沒有面試題,而好點的公司都喜歡問底層原理。python

想必用過selenium的測試朋友都知道18種定位方式吧?web

  

示例腳本準備

若是你去面試自動化,面試官估計會問你webdriver client的原理,由於這樣才能看出你自動化的深度,這須要去研究下源碼。面試

懵逼?別急,下面我就結合源碼給你們簡單介紹下(看主要源碼便可,原本想畫個調用流程圖,可是着實精力有限)chrome

我用的selenium版本是__version__ = '3.0.1'瀏覽器

寫一個簡單的樣例服務器

from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://www.cnblogs.com/uncleyong/')
driver.close()  # 關閉當前窗口,可是沒結束進程
driver.quit()

 

 

 

運行示例,成功打開目標頁面session

 

源碼分析 

打開selenium的目錄結構,webdriver下有各類客戶端瀏覽器,好比chrome,firefox,ie等源碼分析

 

那webdriver client的原理是什麼呢?回到下面的示例,點擊下圖第2行Chrome

 

下面第20行能夠看到,Chrome是一個別名

 

點擊上圖第20行WebDriver

下圖能夠看到,WebDriver類繼承了RemoteWebDriver類,RemoteWebDriver類是as的別名,是另一個py文件中WebDriver類的別名

下面這個WebDriver類是在seleniumwebdriver.chrome下的webdriver.py中

這個類的註釋:Controls the ChromeDriver and allows you to drive the browser,含義是:控制ChromeDriver並容許驅動瀏覽器。

 

實例化WebDriver(也就是執行webdriver.Chrome())的時候,過程當中會先啓動chromedriver,而後調用父類RemoteWebDriver的__init方法

 

上圖57行實例化Service類,點擊這個類

 

點擊上圖35行free_port 

下面是獲取到一個可用的端口

 

執行下圖62行self.service.start()後,啓動了chromedriver

 

點擊上圖62行start

可用看到,subprocess調用命令行打開webdriver.exe

 

 

也就是selenium啓動了chromedriver,能夠看到對應的進程

 

咱們把獲取到的端口打印出來

 

 

 

要執行了self.service.start()後,啓動了chromedriver,下面命令纔有結果

 

咱們來看下RemoteWebDriver這個父類,點擊下圖65行RemoteWebDriver

 

名稱仍是WebDriver,注意,只要不在同一個py文件,類名是能夠相同的,可是若是其中一個要引用另一個,另一個要as取別名,如上圖中from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver

這個類的註釋:Controls a browser by sending commands to a remote server,含義是:經過向遠程服務器發送命令來控制瀏覽器。 

 

來看其__init__方法,

下圖58行__init__中參數command_executor的默認值是http://127.0.0.1:4444/wd/hubremote,

下圖86行建立RemoteConnection類對象也須要參數command_executor,說明是鏈接remote server的URL,RemoteConnection類是負責與Remote WebDriver server鏈接的類

 

點擊上圖92行start_session,

能夠看到,下圖180行生成sessionId

 

點擊上圖179行execute

 

點擊上圖234行execute

command表示執行的命令

 

點擊上圖407行command_info

 

點擊上圖402行_commands,打開self._commands這個dict,下圖199行就是咱們前面的發送請求的命令Command.NEW_SESSION,發送一個路徑爲/session的post請求,生成sessionId,返回響應

 

回到下圖,點擊407行_request

 

在_request方法中,try下面第一行將打開目標瀏覽器,並綁定到指定端口,綁定完成後,該啓動的瀏覽器實例就作爲WebDriver的remote server

resp是一個返回的對象

 

後面執行resp.read(),獲取到對象內的data數據,debug模式下,可用看到,data中包括已經生成的sessionId

 

另外,WebDriver類是在selenium.webdriver.remote下的webdriver.py文件中

 

 

回到咱們的示例腳本

 

 

點擊上方第3行get

 

上圖248行url是咱們傳的參數,點擊上圖248行GET,經過下圖可知Command是一個類,GET是類屬性

 

點擊上上圖248行execute

咱們直接看response後面的代碼,self對象後面是一個名爲command_executor的對象,這個對象執行了execute方法。

 

點擊上圖234行command_executor

下圖86行能夠看到,對象command_executor是RemoteConnection類的實例,

這個實例是在__init__方法中,說明是在建立selenium.webdriver.remote.webdriver.WebDriver類對象的時候也完成了command_executor對象的建立

 

點擊上圖86行RemoteConnection類

RemoteConnection類就是負責與Remote WebDriver server鏈接的類

 

再回到下圖,來看RemoteConnection類的方法execute

 

點擊上圖234行execute

command表示執行的命令

 

點擊上圖407行command_info

 

點擊上圖402行_commands,打開self._commands這個dict,

下圖206行就是咱們前面的發送請求的命令Command.GET,經過sessionId肯定請求的瀏覽器,也就是說sessionId表示了remote server和對應瀏覽器的一個會話,指令經過這個會話變成對瀏覽器的一個操做,

remote server在執行完對瀏覽器的操做後獲得的數據將做爲響應的body返回給測試代碼。

另外,216行是經常使用的

 

手工模擬 

爲了更加清晰查看請求過程當中的path、params、method、url、body值,咱們先本身寫一個mylogger文件

修改源文件remote_connection.py

 

點擊上圖411行_request,添加以下內容

 

運行示例,控制檯打印出日誌

 

完整版日誌:

[2019-11-12 22:49:05,821] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : execute:408 , 【path】:/session ----- 【params】:{'desiredCapabilities': {'browserName': 'chrome', 'version': '', 'platform': 'ANY', 'javascriptEnabled': True, 'chromeOptions': {'extensions': [], 'args': []}}, 'requiredCapabilities': {}}

[2019-11-12 22:49:05,821] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : _request:428 , 【method】:POST +++++ 【url】:http://127.0.0.1:21753/session +++++ 【body】:{"desiredCapabilities": {"browserName": "chrome", "version": "", "platform": "ANY", "javascriptEnabled": true, "chromeOptions": {"extensions": [], "args": []}}, "requiredCapabilities": {}}

[2019-11-12 22:49:07,882] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : execute:408 , 【path】:/session/80c0d4efaeea62ae67c11a9ae3c656fd/url ----- 【params】:{'url': 'https://www.cnblogs.com/uncleyong/', 'sessionId': '80c0d4efaeea62ae67c11a9ae3c656fd'}

[2019-11-12 22:49:07,882] [DEBUG] [C:\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py : _request:428 , 【method】:POST +++++ 【url】:http://127.0.0.1:21753/session/80c0d4efaeea62ae67c11a9ae3c656fd/url +++++ 【body】:{"url": "https://www.cnblogs.com/uncleyong/", "sessionId": "80c0d4efaeea62ae67c11a9ae3c656fd"}

 

經過debug調試可知, 執行2-5行代碼時,driver_command的值分別爲:newSession、get、close、quit

 

上面日誌中,

前兩行是執行response = self.execute(Command.NEW_SESSION, capabilities)產生的日誌

後兩行是執行self.execute(Command.GET, {'url': url})產生的日誌

 

還原上面第2-3行過程

手動執行chromedriver.exe,模擬selenium先啓動了chromedriver(不要手動關閉)

 

模擬上面日誌中第一個post請求,browserName是chrome,表示告訴remote driver打開chrome瀏覽器

打開了瀏覽器(不要手動關閉) 

 

  

 響應中,返回了sessionId,後面和瀏覽器的交互都是基於這個sessionId進行的

 

 

 

模擬上面日誌第二個post請求,sessionId替換爲上面返回的值:8832915b0bc0151aab6573ab33b4be93

 

瀏覽器展現了咱們的目標url頁面內容

 

響應status等於0,即表示命令執行成功

 

結論 

看懂上面的分析,你應該知道結論了吧?

若是仍是迷糊,那請關注公衆號「全棧測試筆記」,回覆「webdriver」獲取結論。

相關文章
相關標籤/搜索