前言
又到年底了,群里很多朋友說要開始備戰2020金三銀四,其實,我建議是,如果你不是技術大牛,就不要去湊熱鬧。
其實,現在(11,12月份)就是最佳換工作的時候,因為很多人想等着拿了年終再走,雖然招聘的公司不多,但是找工作的更少,所以,可以去試試有沒有好機會。
很多人說要去面試自動化,問我有沒有面試題,而好點的公司都喜歡問底層原理。
想必用過selenium的測試朋友都知道18種定位方式吧?
示例腳本准備
如果你去面試自動化,面試官估計會問你webdriver client的原理,因為這樣才能看出你自動化的深度,這需要去研究下源碼。
懵逼?別急,下面我就結合源碼給大家簡單介紹下(看主要源碼即可,本來想畫個調用流程圖,但是着實精力有限)
我用的selenium版本是__version__ = '3.0.1'
寫一個簡單的樣例
from selenium import webdriver driver = webdriver.Chrome() driver.get('https://www.cnblogs.com/uncleyong/') driver.close() # 關閉當前窗口,但是沒結束進程 driver.quit()
運行示例,成功打開目標頁面
源碼分析
打開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,即表示命令執行成功
結論
看懂上面的分析,你應該知道結論了吧?
如果還是迷糊,那請關注公眾號“全棧測試筆記”,回復“原理”獲取結論。