背景
在自動化實施中我們可能會選擇一定的方案來針對業務。現在來看業務服務形式主要是web和wap,所以選擇了selenium開源自動化測試工具來滿足我們的自動化需求。但是selenium自身設計並不能滿足我們所有的應用場景,如:get方法添加header。selenium原生設計就不支持這個。
所以問題是:selenium如何支持header操作,來滿足業務自動化場景?
解決思路
Webdriver不包含用於執行此操作的API。有關詳細信息,請參閱Selenium跟蹤器的問題141。該問題的標題說它是關於響應標頭但是決定Selenium在此問題的範圍內不包含請求標頭的API。
有關添加API以設置請求標頭的幾個問題已標記爲重複:第一,第二,第三。
以下幾種可能性:
- 使用另一個驅動程序/庫而不是selenium(這個還要看編程所在領域給的庫或包:python selenium-wire )
- 編寫特定於瀏覽器的插件(或查找現有插件),允許您爲請求添加標頭。(現在好像是有插件:chrome和modify-headers firefox,但是對瀏覽器版本有要求)
- 使用browsermob-proxy或其他一些代理。(代理是個好主意)
- Selenium沒有內置方法來執行此操作,但可以通過使用fiddler等代理來完成。Fiddler還提供了FiddlerCore組件的僅API版本,以及對所有代理設置和數據的編程訪問,從而允許您修改http響應的標頭。(同第三代理很像,基本是一樣的)
在大多數情況下,會選擇3:爲什麼? 這個取決於你的selenium的設計(二次開發)
。
請注意,Ghostdriver有一個API,但其他驅動程序不支持它。
關於browserMob
通常情況下,直接使用Selenium就足夠了,但是Selenium有很多自身的問題,比如不支持修改request的參數,比如很重要的headers。headers其實是一些模擬測試時用來模擬不同的訪問和測試安全的重要元信息,從一些github issues看Selenium開發方似乎拒絕考慮加入headers修改功能,以自動化測試組件不應該讓用戶修改headers的理由應付用戶,並讓大家使用BrowserMobProxy去模擬。如果webdriver也是可控的,這完全是可以做到的,可能會需要協調不同瀏覽器的webdriver開發者會有點麻煩;所以也不知道是開發方懶,還是和browsermobproxy的開發方有什麼關係。。而彈出瀏覽器並能提供足夠的控制功能的框架目前暫沒見到其他能與selenium媲美的。(PhantomJS是在服務端提供了個解析,但並不會事實彈出瀏覽器模仿真正的瀏覽器行爲,也就是說一些前端涉及鼠標、懸停等事件的腳本並不能支持)
browserMob-proxy 官網
browserMob-proxy GitHub
這個browserMob-prox支持兩種用法:
一是嵌入式
:把代理服務寫到case裏或者自定義的selenium服務裏,這裏可能會有一個問題,就是服務容器的衝突(browserMob用的好像是jetty,如果你的selenium服務是tomcat的就會有報錯);
二是使用遠程方式
,browserMob-proxy服務獨立部署,腳本動態綁定代理並使用;
在靈活性上、獨立性上,我們會選擇第二種方式:遠程訪問
實踐: browserMob遠程代理
即browsermob在外部服務獨立運行,本地腳本或者selenium服務只通過restful與其通信,期間包括先註冊端口和設置headers等,seleniumProxy也得配置到對應的socket去。
部署browsermob也很簡單,官網 下載部署版本或從github下載項目tag版本命令行執行 mvn clean package -U
打包,然後找到對應的jar文件進行源碼安裝。源碼安裝的話在browsermob-dist/target/ 下可以找到bin結尾的目錄,裏面就是可執行文件(*nix和windows bat都有),copy到你需要的目錄即可。
我們選擇最直接的部署方式:下載已打包好的,直接進行部署
bin
目錄下有bat和shell文件,直接執行就行
readme.md
文件裏有教程:如:browsermob-proxy -port 8080
啓動當前服務
[INFO 2019-06-13T15:28:18,818 net.lightbody.bmp.proxy.Main] (main) Starting BrowserMob Proxy version 2.1.4
[INFO 2019-06-13T15:28:18,851 org.eclipse.jetty.util.log] (main) jetty-7.x.y-SNAPSHOT
[INFO 2019-06-13T15:28:18,938 org.eclipse.jetty.util.log] (main) started o.e.j.s.ServletContextHandler{/,null}
[INFO 2019-06-13T15:28:19,075 org.eclipse.jetty.util.log] (main) Started [email protected]:8080
[INFO 2019-06-13T15:28:26,987 org.littleshoot.proxy.impl.DefaultHttpProxyServer] (qtp1894788146-21) Starting proxy at address: /****:11011
[INFO 2019-06-13T15:28:27,010 org.littleshoot.proxy.impl.DefaultHttpProxyServer] (qtp1894788146-21) Proxy listening with TCP transport
[INFO 2019-06-13T15:28:27,125 org.littleshoot.proxy.impl.DefaultHttpProxyServer] (qtp1894788146-21) Proxy started at address: /****:11011
[INFO 2019-06-13T15:28:28,151 org.littleshoot.proxy.impl.ProxyToServerConnection] (LittleProxy-0-ProxyToServerWorker-0) (HANDSHAKING) [id: 0x2522675f,
怎麼在用例裏使用呢?(詳閱官網文檔,這裏不贅訴了)
注意:這裏要注意兩個端口號:browserMob作爲一個服務,爲你提供服務的端口是8080
或者其他你自定義的(來自-port
);另一個是你用例腳本使用的端口(如:11011
),你會把你的header及其他規則註冊到這個端口11011
上(這個代理端口是可以動態註冊的),然後腳本上綁定的代理就是這個*.*.*.*:11011
maven: 解決下依賴問題
<dependency>
<groupId>net.lightbody.bmp</groupId>
<artifactId>browsermob-core</artifactId>
<version>${browsermob.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
java:實例代碼(記得引入包)
DesiredCapabilities capabilities = DesiredCapabilities.chrome();
Map<String, Object> chromeOptions = new HashMap<>();
if(isheadless!=null && isheadless == 1){ //是否走無頭瀏覽器
List<String> chromeargslist = new ArrayList<>();
chromeargslist.add("--headless");
chromeOptions.put("args",chromeargslist);
}
// jwensh 處理代理的問題 20190613
if(useProxy == 1){
if(proxyHeaders.trim() != "" && proxyHeaders.trim() != "{}"){
// 向代理服務註冊工作代理端口
Map<String, Object> registerPort = new HashMap<>();
registerPort.put("bindAddress", proxyServerIP);
registerPort.put("port", "11011");
HttpUtil.sendHttpPost(String.format("http://%s:%s/proxy", proxyServerIP, "8080"), registerPort);
try {
//這個就是講header轉化爲header註冊到代理服務中去
JSONObject ph = JSONObject.parseObject(proxyHeaders);
//post請求把我的代理配置註冊到代理裏
HttpUtil.sendJson(String.format("http://%s:%s/proxy/%s/headers", proxyServerIP, "8080", "11011"), ph);
// 指定工作的代理而不是代理服務
String PROXY = proxyServerIP + ":" + "11011";
Proxy seleniumProxy = ClientUtil.createSeleniumProxy(new InetSocketAddress(proxyServerIP, 11011));
seleniumProxy.setHttpProxy(PROXY).setSslProxy(PROXY);
// 註冊代理到瀏覽器屬性上
capabilities.setCapability(CapabilityType.PROXY, seleniumProxy);
logger.info("代理設置完畢!");
}catch (Exception e){
logger.error("config selenium proxy happend error");
}
}
}
if(isheadless!=null && isheadless==1){
capabilities.setCapability(ChromeOptions.CAPABILITY,chromeOptions);
}
return new RemoteWebDriver(weburl,capabilities);
如果你是python selenium
也不用擔心,同樣適用 (pip install browsermob-proxy)
。
總結下
- 使用代理的方式,就像我們平時測試app一樣適用fiddler或Charles來攔截並修改內容一樣,並不會影響我們的服務(browserMob支持https)
- 對於我當前的selenium平臺服務來說,會一直使用同一個代理端口處理多個case且並行的操作,可能會有header內容丟失的可能,但是目前不會,量級還沒有上,如果出現這個問題,可以將代理端口配置成多個
- 代理解決了selenium不支持header的問題;簡單、好用。