Selenium Grid介紹
盡管在未來(lái)將會(huì)推出的Selenium 4.0版本中對(duì)Selenium Grid的一些新特性進(jìn)行了說(shuō)明,但是目前來(lái)看官方并沒(méi)有太多詳細(xì)文檔供大家參考,所以本書(shū)中仍結(jié)合目前被廣泛使用的Selenium Grid 版本進(jìn)行講解。
正如其官網(wǎng)對(duì)Selenium Grid的描述,它是一個(gè)智能代理服務(wù)器,允許Selenium測(cè)試將命令路由到遠(yuǎn)程Web瀏覽器實(shí)例。其目的是提供一種在多臺(tái)計(jì)算機(jī)上并行運(yùn)行測(cè)試的簡(jiǎn)便方法。使用Selenium Grid,一臺(tái)服務(wù)器充當(dāng)將JSON格式的測(cè)試命令路由到一個(gè)或多個(gè)已注冊(cè)Grid節(jié)點(diǎn)的中樞,以獲得對(duì)遠(yuǎn)程瀏覽器實(shí)例的訪問(wèn)。Hub有一個(gè)已注冊(cè)服務(wù)器的列表,它可以訪問(wèn)并允許控制這些實(shí)例。Selenium Grid允許我們?cè)诙嗯_(tái)計(jì)算機(jī)上并行運(yùn)行測(cè)試,并集中管理不同的瀏覽器版本和瀏覽器配置。
圖1 Selenium Grid的組件構(gòu)成圖
如圖1所示,可以看到Selenium Grid主要由2部分構(gòu)成,即:Hub和Nodes。您可以使用Python、Java、C#等語(yǔ)言編寫(xiě)測(cè)試Selenium腳本,每個(gè)Selenium Grid僅有一個(gè)Hub,客戶(hù)端腳本可以指定連接到該Hub(主控節(jié)點(diǎn)或者叫集線器),Hub接收客戶(hù)端腳本的運(yùn)行測(cè)試請(qǐng)求,同時(shí)將這些測(cè)試請(qǐng)求分發(fā)到已注冊(cè)的一個(gè)或多個(gè)節(jié)點(diǎn)去執(zhí)行并收集運(yùn)行結(jié)果。Selenium Grid中可以一個(gè)或多個(gè)Node(節(jié)點(diǎn))。作為節(jié)點(diǎn)的機(jī)器不必與Hub或其他Node具有相同的操作系統(tǒng)或相同的瀏覽器。即:某個(gè)Node節(jié)點(diǎn)可能是Windows操作系統(tǒng),而在該系統(tǒng)上安裝的是Internet Explorer瀏覽器,另外的Node節(jié)點(diǎn)可能用的是Linux、Mac操作系統(tǒng),而它們安裝的瀏覽器可能是Firefox、Safari、Chrome等。這些Node節(jié)點(diǎn)的設(shè)置結(jié)合測(cè)試來(lái)講,就是要看您想做那些操作系統(tǒng)、瀏覽器版本的兼容性測(cè)試了,在實(shí)際工作中請(qǐng)結(jié)合測(cè)試執(zhí)行計(jì)劃和策略進(jìn)行選擇。
基于Docker的Selenium Grid的相關(guān)配置
在Docker Hub中提供了Selenium Grid的相關(guān)鏡像文件可供使用,如圖2所示。
圖2 Selenium Grid的相關(guān)鏡像資源
這里,我們應(yīng)用“docker pull”命令分別將這3個(gè)鏡像拉取下來(lái),對(duì)應(yīng)的拉取命令如下:
docker pull selenium/hub
docker pull selenium/node-chrome
docker pull selenium/node-firefox
鏡像文件拉取到本地后,您可以使用“docker images”命令查看一下相關(guān)鏡像的信息,如圖3所示。
圖3 Selenium Grid的相關(guān)鏡像信息
這里,先來(lái)測(cè)試一下Hub與Node節(jié)點(diǎn)之間的連通性。
啟動(dòng)Hub,如圖4所示。
圖4 創(chuàng)建并啟動(dòng)hub容器
創(chuàng)建并啟動(dòng) chromenode容器節(jié)點(diǎn),如圖5所示。
圖5 創(chuàng)建并啟動(dòng)chromenode容器節(jié)點(diǎn)
創(chuàng)建并啟動(dòng) firefoxnode容器節(jié)點(diǎn),如圖6所示。
圖6 創(chuàng)建并啟動(dòng)firefoxnode容器節(jié)點(diǎn)
接下來(lái),在本機(jī)瀏覽器地址欄輸入“http://localhost:4444/grid/console”,即:打開(kāi)Selenium Grid的控制臺(tái),將出現(xiàn)圖7所示頁(yè)面。
圖7 Grid Console控制臺(tái)信息
從圖7可知,當(dāng)前使用的Selenium Grid 版本為3.141.59版本,對(duì)應(yīng)連接到Hub的兩個(gè)Node節(jié)點(diǎn)分別是IP為172.17.0.4的Linux操作系統(tǒng)使用的是Firefox 75.0版本的瀏覽器和IP為172.17.0.3的Linux操作系統(tǒng)使用的是Chrome 81.0.4044.92版本的瀏覽器。在默認(rèn)情況下Hub節(jié)點(diǎn)使用的是4444端口,而Node節(jié)點(diǎn)在本例中使用的是5555端口,如果在同一個(gè)容器中出現(xiàn)端口沖突等情況,則您需要根據(jù)實(shí)際情況進(jìn)行調(diào)整設(shè)置其他端口以避免端口沖突情況發(fā)生。
基于Docker+Selenium Grid的案例演示
下面,筆者將結(jié)合Bing搜索案例在Chrome和Firefox瀏覽器上實(shí)現(xiàn)兼容性測(cè)試。在經(jīng)過(guò)前面Selenium、Docker和Selenium Grid相關(guān)知識(shí)的學(xué)習(xí)后,您想到了什么?是不是可以通過(guò)使用Docker+Selenium Grid就能夠完成基于不同瀏覽器的兼容性測(cè)試呢?是的,這確實(shí)是個(gè)好主意。
但是,如果讓Selenium測(cè)試腳本在不同瀏覽器中運(yùn)行,又需要做些什么呢?
在腳本設(shè)計(jì)上,您需要做一些改變,通常情況下,要在腳本的運(yùn)行時(shí)指定主機(jī)和端口,腳本類(lèi)似于以下方式:
import time
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
driver = webdriver.Remote(
command_executor='http://192.168.1.102:4444/wd/hub',
desired_capabilities=DesiredCapabilities.CHROME)
base_url = 'https://cn.bing.com'
driver.get(base_url)
driver.save_screenshot('chrome.png')
driver.close()
通常在執(zhí)行時(shí),只需指定Hub的地址(即:http://192.168.1.102:4444/wd/hub)。這里宿主機(jī)的IP如圖8所示, Hub會(huì)將腳本自動(dòng)分配給Node節(jié)點(diǎn)去執(zhí)行。
圖8 宿主機(jī)的IP地址信息
- command_executor參數(shù):該參數(shù)為選填參數(shù),可指定遠(yuǎn)程服務(wù)器URL字符串或自定義遠(yuǎn)程連接,默認(rèn)為“http://127.0.0.1:4444/wd/hub”。
- desired_capabilities參數(shù):該參數(shù)為必填參數(shù),可根據(jù)情況配置啟動(dòng)瀏覽器會(huì)話時(shí)請(qǐng)求功能的字典。這里我們應(yīng)用的是“DesiredCapabilities.CHROME”,您可以查看其對(duì)應(yīng)源代碼如下所示。
class DesiredCapabilities(object):
"""
Set of default supported desired capabilities.
Use this as a starting point for creating a desired capabilities object for
requesting remote webdrivers for connecting to selenium server or selenium grid.
Usage Example::
from selenium import webdriver
selenium_grid_url = "http://198.0.0.1:4444/wd/hub"
# Create a desired capabilities object as a starting point.
capabilities = DesiredCapabilities.FIREFOX.copy()
capabilities['platform'] = "WINDOWS"
capabilities['version'] = "10"
# Instantiate an instance of Remote WebDriver with the desired capabilities.
driver = webdriver.Remote(desired_capabilities=capabilities,
command_executor=selenium_grid_url)
Note: Always use '.copy()' on the DesiredCapabilities object to avoid the side
effects of altering the Global class instance.
"""
FIREFOX = {
"browserName": "firefox",
"acceptInsecureCerts": True,
}
INTERNETEXPLORER = {
"browserName": "internet explorer",
"version": "",
"platform": "WINDOWS",
}
EDGE = {
"browserName": "MicrosoftEdge",
"version": "",
"platform": "ANY"
}
CHROME = {
"browserName": "chrome",
"version": "",
"platform": "ANY",
}
OPERA = {
"browserName": "opera",
"version": "",
"platform": "ANY",
}
SAFARI = {
"browserName": "safari",
"version": "",
"platform": "MAC",
}
HTMLUNIT = {
"browserName": "htmlunit",
"version": "",
"platform": "ANY",
}
HTMLUNITWITHJS = {
"browserName": "htmlunit",
"version": "firefox",
"platform": "ANY",
"javascriptEnabled": True,
}
IPHONE = {
"browserName": "iPhone",
"version": "",
"platform": "MAC",
}
IPAD = {
"browserName": "iPad",
"version": "",
"platform": "MAC",
}
ANDROID = {
"browserName": "android",
"version": "",
"platform": "ANDROID",
}
PHANTOMJS = {
"browserName": "phantomjs",
"version": "",
"platform": "ANY",
"javascriptEnabled": True,
}
WEBKITGTK = {
"browserName": "MiniBrowser",
"version": "",
"platform": "ANY",
}
WPEWEBKIT = {
"browserName": "MiniBrowser",
"version": "",
"platform": "ANY",
}
從DesiredCapabilities類(lèi)的源碼可知“DesiredCapabilities.CHROME”是該類(lèi)定義的一個(gè)字典對(duì)象。
這里筆者采用多線程的方式,實(shí)現(xiàn)分別在Chrome和Firefox瀏覽器執(zhí)行Bing搜索業(yè)務(wù),對(duì)應(yīng)腳本如下。
Grid_Test.py文件內(nèi)容:
from threading import Thread
from selenium import webdriver
from time import sleep,ctime
from selenium.webdriver.common.by import By
def Test_Bing(Host, Browser):
caps = {'browserName': Browser}
driver = webdriver.Remote(command_executor=Host, desired_capabilities=caps)
driver.get('http://www.bing.com')
driver.find_element(By.ID,'sb_form_q').send_keys('異步社區(qū)')
driver.find_element(By.ID,'sb_form_go').click()
PicName=Browser+'_result'+'.png'
driver.save_screenshot(PicName)
assert ('沒(méi)有與此相關(guān)的結(jié)果' not in driver.page_source)
sleep(2)
driver.close()
if __name__ == '__main__':
pcs = {'http://192.168.1.102:4444/wd/hub': 'chrome',
'http://localhost:4444/wd/hub': 'firefox'
}
threads = []
tds=range(len(pcs))
# 創(chuàng)建線程
for host, browser in pcs.items():
t = Thread(target=Test_Bing, args=(host, browser))
threads.append(t)
# 啟動(dòng)線程
for i in tds:
threads[i].start()
for i in tds:
threads[i].join()
從上面的腳本,大家可以看到創(chuàng)建了一個(gè)名稱(chēng)為T(mén)est_Bing()的函數(shù),其包含兩個(gè)參數(shù),分別是主機(jī)和瀏覽器。函數(shù)的執(zhí)行意圖就是根據(jù)遠(yuǎn)程服務(wù)器URL字符串和傳入的瀏覽器名稱(chēng)字符串,在對(duì)應(yīng)的瀏覽器中執(zhí)行搜索業(yè)務(wù),且搜索詞為“異步社區(qū)”,對(duì)執(zhí)行結(jié)果進(jìn)行截圖,截圖的名稱(chēng)為對(duì)應(yīng)瀏覽器名稱(chēng)+“_result.jpg”文件,并對(duì)搜索后的結(jié)果進(jìn)行斷言。需要說(shuō)明的是,這里對(duì)結(jié)果進(jìn)行截圖的目的不僅僅是想看一下結(jié)果,還有一個(gè)很重要的原因是在使用Selenium Grid時(shí),執(zhí)行測(cè)試過(guò)程中不會(huì)出現(xiàn)瀏覽器,所以您看不到執(zhí)行過(guò)程,為了證明結(jié)果的正確性我們也需要截一個(gè)圖證明其確實(shí)是工作了并且執(zhí)行是正確的。如果您還想看到不同的容器在執(zhí)行過(guò)程中的界面,也可以使用VNC Viewer連接到對(duì)應(yīng)容器(但需下載對(duì)應(yīng)的selenium/node-firefox-debug和selenium/node-chrome-debug鏡像文件,debug結(jié)尾的鏡像都帶有VNC服務(wù)端,本機(jī)安裝VNC客戶(hù)端,即可遠(yuǎn)程連接。5900端口為VNC Viewer的監(jiān)聽(tīng)端口,故做了一個(gè)端口映射),如圖9和圖10所示。
圖9 創(chuàng)建并啟動(dòng)Debug版本的節(jié)點(diǎn)容器
圖10 VNC Viewer觀察節(jié)點(diǎn)容器的腳本執(zhí)行情況
事實(shí)上這對(duì)于測(cè)試工作并沒(méi)有太多意義,故不做太多文字贅述。
在主函數(shù)中,定義了一個(gè)包含2個(gè)元素的字典,大家可以看到筆者使用了2種同一個(gè)地址不同的表示方式(宿主機(jī)的IP地址為192.168.1.102),而“l(fā)ocalhost”也表示本機(jī),即宿主機(jī)。那么為什么不都用“192.168.1.102”或者“l(fā)ocalhost”呢?這是因?yàn)樽值涞逆I(Key)是不允許重復(fù)的。接下來(lái)創(chuàng)建了一個(gè)線程列表,以pcs字典的鍵、值作為T(mén)est_Bing()函數(shù)的參數(shù),并添加到線程列表。而后啟動(dòng)線程列表中的各線程。
在運(yùn)行腳本前,需保證創(chuàng)建并啟動(dòng)Hub和Node節(jié)點(diǎn)容器(注:這里筆者應(yīng)用的為非Debug版本Node鏡像),如圖11所示。
圖11 創(chuàng)建并啟動(dòng)Hub和Node節(jié)點(diǎn)容器
腳本執(zhí)行完成后,將會(huì)生成“chrome_result.jpg”和“firefox_result.jpg”這2個(gè)圖片文件,如圖12所示。
圖12 腳本執(zhí)行完成后生成的圖片文件信息
如圖13和圖14所示,在本次兼容性測(cè)試中大家可以看到這2個(gè)瀏覽器都執(zhí)行了相同的Bing搜索業(yè)務(wù),它們的頁(yè)面展示、布局、內(nèi)容基本是相同的,但是卻存在2個(gè)小的問(wèn)題,就是在Chrome瀏覽器中搜索到的結(jié)果為“855,000 Results”,而Firefox瀏覽器中搜索到的結(jié)果為“859,000 Results”,它們是不一致的。另一個(gè)小問(wèn)題是,在Firefox瀏覽器會(huì)顯示“Sign in”和一個(gè)登陸圖標(biāo),而在Chrome瀏覽器中卻沒(méi)有。理論上來(lái)講這是2個(gè)嚴(yán)重級(jí)別較低的小Bug,但筆者建議針對(duì)這兩個(gè)小的差異,需要和產(chǎn)品、研發(fā)的同學(xué)再確認(rèn)一下,產(chǎn)品、測(cè)試、研發(fā)應(yīng)統(tǒng)一、明確需求,明確后再修改需求或代碼,使兩者保持一致。
圖13 chrome_result.jpg圖片文件信息
圖14 firefox_result.jpg圖片文件信息
到此這篇關(guān)于基于Docker+Selenium Grid的測(cè)試技術(shù)應(yīng)用示例代碼的文章就介紹到這了,更多相關(guān)Docker Selenium Grid測(cè)試技術(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!