背景
Selenium 是一個Web自動化測試的組件,可基於WebDriver去控制彈出瀏覽器去做一系列Web點擊或行為測試(當然也可以去做一些邪惡的事。。),減少重復人工網頁測試的開銷。BrowserMobProxy相當於一層代理,它配合Selenium使用可以在Selenium控制瀏覽器訪問之前在代理層攔截做出一些記錄(har)、修改等。
一般來說,直接使用Selenium就足夠了,但是Selenium有個很致命的問題是不支持修改request的參數,比如很重要的headers。headers其實是一些模擬測試時用來模擬不同的訪問和測試安全的重要元信息,從一些github issues看Selenium開發方似乎拒絕考慮加入headers修改功能,以自動化測試組件不應該讓用戶修改headers的理由應付用戶,並讓大家使用BrowserMobProxy去模擬。如果webdriver也是可控的,這完全是可以做到的,可能會需要協調不同瀏覽器的webdriver開發者會有點麻煩;所以也不知道是開發方懶,還是和browsermobproxy的開發方有什么關系。。而彈出瀏覽器並能提供足夠的控制功能的框架目前暫沒見到其他能與selenium媲美的。(PhantomJS是在服務端提供了個解析,但並不會事實彈出瀏覽器模仿真正的瀏覽器行為,也就是說一些前端涉及鼠標、懸停等事件的腳本並不能支持)
介紹一下Selenium3.x里幾個概念:
- WebDriver:可以理解為連接不同瀏覽器的驅動程序,比如chrome和firefox的webdriver是不同的,如果selenium相關包沒有引入你的瀏覽器,就要考慮去尋找了。內置支持的瀏覽器參見github。像IE這種需要windows相關組件支持可參考官網配置步驟。
- Selenium IDE:其實就是個可視化的測試案例創建管理的組件,一般我們用selenium可能是寫代碼,但部分沒那么復雜的測試功能可以通過該IDE去配置,也降低了QC的門檻。使用代碼去模擬測試的可忽略IDE的存在。
- Selenium Server:一般只會在遠程測試的時候需要。比如公司有台測試機,你想要在上面測試但又不能在上面直接開發,你就可以在測試機起SeleniumServer,本地測試代碼通過RemoteWebDriver的形式去連接它。大多數情況下本地測試可以忽略這個server的存在。
BrowserMobProxy會提供一個ProxyServer用於做轉發代理攔截,這個server可以是standalone部署支持遠程,也可以embed進代碼中。由於BrowserMob是Java開發的,因此JVM的可以支持真正的embedded,python等非JVM系的只能配置其執行路徑通過子進程的方式來偽裝embedded,這就是AutomatedTester/browsermob-proxy-py項目中需要配置 /path/to/browsermobproxy 的原因。
使用
Selenium WebDriver的具體使用請參考網上的教程示例和官方文檔,此處不贅述。
BrowserMobProxy官方文檔里有段 use with selenium 的代碼示例其實就是 embedded browsermob + local selenium :
// start the proxy BrowserMobProxy proxy = new BrowserMobProxyServer(); proxy.start(0); // set custom headers proxy.addHeaders(headers); // get the Selenium proxy object Proxy seleniumProxy = ClientUtil.createSeleniumProxy(proxy); // configure it as a desired capability DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability(CapabilityType.PROXY, seleniumProxy); // start the browser up WebDriver driver = new FirefoxDriver(capabilities); // open yahoo.com driver.get("http://yahoo.com");
需留意的是,Firefox的webdriver有坑,貌似Firefox>=52.0版本的需要用最新的Selenium和最新的Webdriver(3.3.0+)。本文主要以Java的代碼為示例,python用法也類似(需先 pip install browsermob-proxy)。
remote browsermob + local selenium
executeCMD(String.format("curl -X POST -d 'port=%d' http://localhost:8080/proxy", 55555)); executeCMD(String.format("curl -X POST -H \"Content-disposition:json/application\" - H \"Content-type:json/application\" -d '%s' http://localhost:8080/proxy/" + (port1) + "/headers", headersJson)); // get the Selenium proxy object
String PROXY = "localhost:8080"; Proxy seleniumProxy = ClientUtil.createSeleniumProxy(new InetSocketAddress("localhost", 8080)); seleniumProxy.setHttpProxy(PROXY).setSslProxy(PROXY); // configure it as a desired capability DesiredCapabilities capabilities = DesiredCapabilities.chrome(); capabilities.setCapability(CapabilityType.PROXY, seleniumProxy); // start the browser up WebDriver driver = new ChromeDriver(capabilities);
即browsermob在外部起,本地只能通過restful與其通信,包括先注冊端口和設置headers等,seleniumProxy也得配置到對應的socket去。部署browsermob也很簡單,http://bmp.lightbody.net/ 下載部署版本或從github下載項目tag版本命令行執行 mvn clean package -U 源碼安裝。源碼安裝的話在browsermob-dist/target/ 下可以找到bin結尾的目錄,里面就是可執行文件(*nix和windows bat都有),copy到你需要的目錄即可。
embedded browsermob + remote selenium server
BrowserMobProxy proxyServer = new BrowserMobProxyServer(); proxyServer.addHeaders(headers); proxyServer.start(port); String PROXY = "localhost:" + port; Proxy seleniumProxy = ClientUtil.createSeleniumProxy(new InetSocketAddress("localhost", port)); seleniumProxy.setHttpProxy(PROXY).setSslProxy(PROXY); // configure it as a desired capability DesiredCapabilities capabilities = DesiredCapabilities.chrome(); capabilities.setCapability(CapabilityType.PROXY, seleniumProxy); // 默認selenium server起在4444端口,可在capabilities中配置端口 WebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444"), capabilities);
同樣需去Selenium官網下載standalone-server的jar包,下下來后java -jar xxx.jar 即可運行,相關參數配置見文檔。須注意的是,3.x的selenium server需要jdk8支持,2.x只需要jdk1.7支持。
其實如同以上例子,可根據需求自由組合browsermob和selenium的使用。browsermob 2.x的ProxyServer換成了BrowserMobProxyServer,但其實接口大同小異,它的filter概念有點類似之前的interceptor概念,攔截修改了request后返回null就可以了(不需要按照方法聲明中的返回值真的構造一個response),官網有filter的例子。
maven依賴:
<dependency> <groupId>net.lightbody.bmp</groupId> <artifactId>browsermob-core</artifactId> <version>${browsermob.version}</version> <exclusions> <exclusion> <artifactId>guava</artifactId> <groupId>com.google.guava</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>net.lightbody.bmp</groupId> <artifactId>browsermob-legacy</artifactId> <version>${browsermob.version}</version> <exclusions> <exclusion> <artifactId>guava</artifactId> <groupId>com.google.guava</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency>
guava會有沖突,需exclude。selenium版本3.3.0, browsermob 2.1.4 。
修改headers支持
其實以上示例中的addHeaders函數就可以修改headers了,親測可用。但是需注意,你在瀏覽器看到的request headers不代表最終的headers,你最好用個 php頁面 把server端真正的request打印出來才能看到效果。相關解釋。可以自己做個試驗證實,比如把Host或者一些重要字段隨便填寫,去訪問baidu等,你會發現訪問不了,改回去headers就可以了。