Selenium Webdriver重新使用已開啟的瀏覽器例項

wwwqjpcom發表於2016-04-24

2019-01-28更新:
最新更新了一下,可參考 myReuseWebDriver,簡單測試過Google Chrome 71, Firefox 64,IE11。
2018-2-10更新:
新增了適用於Selenium3.8.1+FireFox57的Python版本的實現
https://github.com/ANBUZHIDAO/myFirefoxDriver

2017-12-23更新:
適用於Selenium3.8.1+FireFox57
https://github.com/ANBUZHIDAO/myFirefoxDriver

本文中的樣例均使用SoapUI ,關於SoapUI+Webdriver 的配置,請看上一篇:
http://blog.csdn.net/wwwqjpcom/article/details/51174664

我弄這個的本意是為了在SoapUI中更好地編寫自動化用例,因為我的業務流程有的很長,有7-8個頁面。
我想把程式碼不集中在一個Groovy 指令碼里,想在第二個指令碼中繼續使用第一個指令碼中開啟的瀏覽器。這樣便於
維護和定位問題。
也還有一種情況是我開啟了瀏覽器,,操作了系統到某一個介面後,我寫了這個頁面的測試指令碼,使用已
開啟的瀏覽器我立刻就可以單獨對這個頁面進行測試,測試我寫的程式碼是否OK 。不通過就人工操作復位頁面,
修改程式碼後再次測試,不用每次測試程式碼是否可行都從頭打卡瀏覽器,登入系統,重新操作了。可以實現分步
單頁面除錯自動化指令碼。

首先,來簡單看一下Selenium Webdriver如何工作的。
(1)Selenium程式碼呼叫API實際上根據 The WebDriver Wire Protocol 傳送不同的Http Request 到 WebdriverServer。
IE 是 IEDriverServer.exe
Chrome是ChromerDriver,下載地址: https://sites.google.com/a/chromium.org/chromedriver/downloads
Firefox是以外掛的形式,直接在selenium-server-standalone-XXX.jar裡了:
webdriver.xpi (selenium-server-standalone-2.48.2.jar中/org/openqa/selenium/firefox/目錄下)

new FirefoxDriver()時,啟動Firefox瀏覽器時,帶此外掛一起啟動,然後外掛會預設監聽7055埠,7055被佔用就使用下一個埠。如下圖所示。
這裡寫圖片描述
同一臺機器上可以同時啟動多個FirefoxDriver例項,每個例項佔用不同的埠號。

The WebDriver Wire Protocol 協議的具體內容請看:https://code.google.com/p/selenium/wiki/JsonWireProtocol#Introduction。  這個協議現在正在被W3C標準化,W3C Webdriver,兩者基本一樣。  
W3C Webdriver標準協議內容:http://www.w3.org/TR/webdriver/

(2)WebdriverServer接收到Http Request之後,根據不同的命令在作業系統層面去觸發瀏覽器的”native事件“,
模擬操作瀏覽器。WebdriverServer將操作結果Http Response返回給程式碼呼叫的客戶端。

為了更清晰直觀地看到這個是如何運轉的,我們來在使用OWASP ZAP做代理,截獲Http Request和Response來看一下。
首先安裝OWASP ZAP或其他有代理功能的工具,設定SoapUI Proxy,如ZAP預設使用8080埠,則SoapUI配置如下:
這裡寫圖片描述
配置完SoapUI埠後,好像需要重啟SoapUI,然後在SoapUI 的自動化測試程式碼中,代理才能正常工作。

重新跑前一節FirefoxDriver的程式碼,檢視截獲的請求和響應。如下圖所示:
這裡寫圖片描述
這裡寫圖片描述
可以清楚地看到程式碼與DriverServer之間是如何根據WebDriver協議連通的。
WebDriver協議是RESTful風格的。

回到我們的主題:Webdriver重新使用已開啟的瀏覽器例項。
Webdriver例項都將重新開啟一個瀏覽器,新建一個Session。Selenium並沒有介面或方法可以使用已有Session。
如果在一個測試用例中不將瀏覽器退出關閉,想要在另一個新的測試用例中使用已開啟的瀏覽器的話,怎麼辦?
Selenium Webdriver本省並沒有這種介面。本人自己實現了一個基於FirefoxDriver的,因為我基本只用Firefox,
IE 什麼的,沒需求,沒動力啊,但是大概原理應該類似,差不多。

FirefoxDriver是主要是由startClient()和startSession()完成初始化。startClient()中開啟瀏覽器,設定執行器HttpCommandExecutor。
StartSession執行New Session指令,獲取SessionID,並根據響應,設定capabilities。
具體有興趣的可以自己下載原始碼,自己看。

根據上文,Webdriver 想要執行命令,需要1:WebdriverServer的地址 2:一個可用的SessionID
寫一個自己的WebDriver類來new一個Webdriver。

因此需要在一個用例中儲存WebdriverServer的地址和SessionID ,最後不退出關閉瀏覽器。
另一個用例中使用儲存的引數,完成Webdriver的例項化。
程式碼就不貼在文章裡了:原始碼及jar包下載地址:
http://download.csdn.net/detail/wwwqjpcom/9500777

其中的初始化程式碼如下(需要兩個引數 Webdriver 的地址和SessionID):

	public myFirefoxDriver(String localserver,String sessionID){
		
		mystartClient(localserver);
		mystartSession(sessionID);
		
	}

程式碼例子:
測試用例1中開啟瀏覽器,但是不退出關閉瀏覽器:

import org.openqa.selenium.By
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebElement
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.support.ui.ExpectedCondition
import org.openqa.selenium.support.ui.WebDriverWait
import org.openqa.selenium.OutputType
import org.apache.commons.io.FileUtils
import org.openqa.selenium.Keys

WebDriver driver = new FirefoxDriver()   
 
try
{
driver.get("https://learnsoapui.wordpress.com") // Url to be opened

//下面兩行將所需的地址和SessionID 儲存起來。樣例因為是在SoapUI中的兩個Step,所以儲存為了SoapUI中  
//用例級別的屬性,具體請根據自己的使用環境儲存為系統引數或其他地方
testRunner.testCase.setPropertyValue( "DriverServer", driver.getCommandExecutor().getAddressOfRemoteServer().toString() )
testRunner.testCase.setPropertyValue( "CaseSession", driver.getSessionId().toString() )
log.info driver.getSessionId().toString()
 
 WebElement element = driver.findElement(By.id("s"))
 element.sendKeys("Assertion")
 
 File f1 = driver.getScreenshotAs(OutputType.FILE)
 FileUtils.copyFile(f1, new File("c:\\screenshot1.png")); // Location to save screenshot
 
 element.submit()
 
 driver.getKeyboard ().pressKey (Keys.DOWN)
 driver.getKeyboard ().pressKey (Keys.DOWN)
 driver.getKeyboard ().pressKey (Keys.DOWN)
 driver.getKeyboard ().pressKey (Keys.UP)
 driver.getKeyboard ().pressKey (Keys.UP)
 driver.getKeyboard ().pressKey (Keys.UP)
}
catch(Exception e)
{
log.info "Exception encountered : " + e.message
}

測試用例2中繼續使用1中已開啟的瀏覽器:

import webtest.myFirefoxDriver;
import org.openqa.selenium.By
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebElement
import org.openqa.selenium.firefox.FirefoxDriver
import org.openqa.selenium.JavascriptExecutor

//下面三行,取出儲存的可用的瀏覽器的Webdriver Server的地址和SessionID,new一個Webdriver。
def driverserver = testRunner.testCase.getPropertyValue( "DriverServer" )
def caseSession = testRunner.testCase.getPropertyValue( "CaseSession" )
WebDriver driver = new myFirefoxDriver(driverserver,caseSession)

log.info (driver.getCommandExecutor().getAddressOfRemoteServer())

try
{
driver.findElement(By.linkText("Home")).click()// Url to be opened
driver.findElement(By.linkText("About Author")).click()// Url to be opened
 log.info driver.getSessionId().toString()
 log.info driver.getCapabilities()

((JavascriptExecutor)driver).executeScript("alert(\"hello,this is a alert!\")");
 //driver.quit()
}
catch(Exception e)
{
log.info "Exception encountered : " + e.message
}

這裡寫圖片描述
注意程式碼中的文字註釋下方部分,1中要將WebdriverServer的地址和SessionID 儲存起來,
2中使用1中儲存的引數,用實現的自己的myFirefoxDriver來初始化driver。
(此處感謝https://learnsoapui.wordpress.com/ , 我最初研究在SoapUI中使用Selenium
Webdriver看了這個,雖然感覺他講的也是不明不白的 ,但是給了我啟發)。

注:(1)webtest01.jar是我在Selenium 2.48.2版本的程式碼下編譯的,本人只配合用過Selenium 2.48.2   
       和Selenium 2.53.0這兩個版本可用,其他的Selenium版本我是沒試過的,說不定老版本不支援的。)
   (2)實際中myFirefoxDriver不僅可以在本地用,用來RemoteWebdriver遠端呼叫也是可以用的,即開啟  
       RemoteWebdriver,然後用myFirefoxDriver來繼續使用遠端的那個已開啟的瀏覽器例項,反正是隻需要那兩  
       個引數就可以,反正我自己用的時候這種情況也是是可以用的。僅供參考,不提供技術支援,呃,後果自負哦。

相關文章