WebDriver設(shè)計(jì)
WebDriver的API被定位為“基于對(duì)象的”。接口被明確定義并努力堅(jiān)持只包含一個(gè)角色或者責(zé)任,而不是將每一個(gè)可能的HTML標(biāo)記模塊化為單獨(dú)的類,只有一個(gè)WebElement接口。通過(guò)這種方式,開發(fā)人員使用支持自動(dòng)補(bǔ)全的IDE即可被提示下一步工作。其結(jié)果類似于下面的代碼片段(Java語(yǔ)言):
WebDriver driver = new FirefoxDriver();
driver.<user hits space>
此時(shí),包含13個(gè)方法的短列表顯示出來(lái),用戶選擇其中一個(gè):
driver.findElement(<user hits space>)
大多數(shù)IDE現(xiàn)在顯示預(yù)期參數(shù)的類型提示,在這個(gè)例子中是“By”。By包含許多預(yù)定義的靜態(tài)工廠方法,用戶可以快速地完成剛才的代碼:
driver.findElement(By.id("some_id"));
基于角色的接口
WebDriver廣泛使用了基于角色的接口。例如,有一個(gè)JavascriptExecutor接口提供了在當(dāng)前頁(yè)面環(huán)境中執(zhí)行任意Javascript語(yǔ)句塊的功能。WebDriver實(shí)例對(duì)該接口的成功映射可以提示你利用該方法完成自己的工作。
處理大量的組合
假設(shè)X種瀏覽器和Y種語(yǔ)言,我們很容易會(huì)掉進(jìn)X×Y種實(shí)現(xiàn)中,將會(huì)面臨維護(hù)成本的大量攀升。
減少WebDriver支持的編程語(yǔ)言種類是降低成本的途徑之一,但是基于兩種原因不想這樣做。首先,從一種語(yǔ)言切換到另一種時(shí)人們會(huì)承受認(rèn)知負(fù)荷,因此對(duì)用戶來(lái)說(shuō)如果測(cè)試框架(WebDriver)能夠允許他們采用在日常開發(fā)中使用的編程語(yǔ)言來(lái)編寫測(cè)試,那么這是巨大的優(yōu)勢(shì)。其次,在單一項(xiàng)目中混合多種語(yǔ)言可能會(huì)讓團(tuán)隊(duì)很不方便,而且公司的編碼規(guī)范和需求通常要求技術(shù)單一純正性,因此,減少支持語(yǔ)言的種類不是可選項(xiàng)。
減少支持瀏覽器的數(shù)量也不是一種選擇——當(dāng)項(xiàng)目組決定在WebDriver中淘汰對(duì)Firefox 2的支持時(shí),遇到了強(qiáng)烈的抗議,而事實(shí)上當(dāng)時(shí),F(xiàn)irefox 2只占了瀏覽器市場(chǎng)份額不到1%。
的選擇是努力使所有瀏覽器對(duì)語(yǔ)言的綁定看起來(lái)相同:它們應(yīng)該提供統(tǒng)一的接口,可以輕松地通過(guò)各種語(yǔ)言解決。更重要的是,希望語(yǔ)言綁定本身盡可能的易于編寫,這意味著需要盡可能的使它們保持簡(jiǎn)潔。在底層driver中放入了盡可能多的邏輯來(lái)支持這種設(shè)計(jì):放棄放入dirver的每一塊功能都意味著需要通過(guò)支持的每一種語(yǔ)言實(shí)現(xiàn),而這代表了大量的工作量。
WebDriver設(shè)計(jì)中的缺陷
通過(guò)這種方式發(fā)布功能的缺陷在于除非有人知道某個(gè)特定的接口存在,否則他們可能不會(huì)意識(shí)到WebDriver支持這種功能,在API的可發(fā)掘性上存在缺憾。當(dāng)然在WebDriver剛發(fā)布的時(shí)候,我們會(huì)投入大量時(shí)間來(lái)指導(dǎo)人們找到合適的接口,F(xiàn)在我們已經(jīng)花費(fèi)大量精力來(lái)編寫文檔,隨著API獲得廣泛應(yīng)用,用戶會(huì)越來(lái)越容易的找到所需的文檔。
從實(shí)現(xiàn)者的觀點(diǎn)來(lái)看,緊密綁定瀏覽器也是一種設(shè)計(jì)缺陷,雖然無(wú)法避免。支持新瀏覽器時(shí)需要投入巨大的努力,經(jīng)常需要數(shù)次嘗試才能找到正確方法。具體的例子是,Chrome驅(qū)動(dòng)經(jīng)過(guò)了四次完全重寫,IE驅(qū)動(dòng)也有三種關(guān)鍵重寫。緊密綁定瀏覽器的優(yōu)點(diǎn)在于它提供了更多控制權(quán)。
遠(yuǎn)程Webdriver協(xié)議的第二次迭代因此使用了HTTP傳輸機(jī)制和UTF-8編碼的JSON作為默認(rèn)的編碼,使客戶端可以很容易的使用各種有限支持Unicode的語(yǔ)言,因?yàn)閁TF-8向后兼容ASCII。發(fā)送給服務(wù)器的命令使用URL以確定哪個(gè)命令被發(fā)送,并編碼在一個(gè)數(shù)組中的命令的參數(shù)。
例如,一個(gè)命令WebDriver.get("http://www.example.com")映射到一個(gè)到URL的POST請(qǐng)求,編碼了回話ID和以”/”結(jié)束,使用像{[}'http://www.example.com'{]}這樣的參數(shù)的陣列。返回的結(jié)果更有結(jié)構(gòu)性,有一個(gè)返回值和錯(cuò)誤碼的代碼占位。不久到了第三次遠(yuǎn)程協(xié)議迭代,使用命名參數(shù)字典取代了參數(shù)請(qǐng)求數(shù)組。這有利于更方便的調(diào)試請(qǐng)求,消除了客戶端失誤使用了參數(shù)的可能性,使得系統(tǒng)更健壯。當(dāng)然,決定使用正常的HTTP錯(cuò)誤碼表示確定的返回值和響應(yīng),例如,如果用戶試圖調(diào)用一個(gè)沒(méi)有映射到的URL,或者當(dāng)我們想表示空響應(yīng)時(shí),這是種合適的方式。
Webdriver遠(yuǎn)程協(xié)議有兩個(gè)錯(cuò)誤處理層次,一個(gè)無(wú)效的請(qǐng)求和一個(gè)失敗的命令。無(wú)效的請(qǐng)求的例子是請(qǐng)求一個(gè)服務(wù)器并不存在的資源,或者可能是資源不理解,(例如發(fā)送一個(gè)DELETE命令給用于處理當(dāng)前頁(yè)面的URL的資源)。這種情況下,一個(gè)正常的HTTP響應(yīng)是4XX。對(duì)于失敗的命令,錯(cuò)誤碼設(shè)置為500(服務(wù)器端錯(cuò)誤),并且返回的數(shù)據(jù)包含更詳細(xì)的錯(cuò)誤分解。
當(dāng)包含數(shù)據(jù)的響應(yīng)從服務(wù)器發(fā)出后,它是一個(gè)JSON對(duì)象的格式:
Key 描述
Sessionid 服務(wù)器使用的決定何處路由特定會(huì)話命令的不透明處理
狀態(tài)值 一個(gè)概括命令結(jié)果數(shù)字狀態(tài)碼,非零值代表命令失敗響應(yīng)的JSON值
例如:
FirefoxDriver.prototype.getElementAttribute =
function(respond, parameters) {
var element = Utils.getElementAt(parameters.id, respond.session.getDocument());
var attributeName = parameters.name;
respond.value = webdriver.element.getAttribute(element, attributeName);
respond.send();
};
因?yàn)榭梢,我們?huì)在響應(yīng)中編碼狀態(tài)碼,使用非零值表示發(fā)生了糟糕的事故。首先在IE driver上使用了狀態(tài)碼,而且被用于了線協(xié)議。因?yàn)樗械腻e(cuò)誤碼在不同的driver中一致的,在不同的driver中可以使用特殊的語(yǔ)言完成共享錯(cuò)誤處理的代碼,使客戶端更容易實(shí)現(xiàn)。
遠(yuǎn)程webdriver服務(wù)簡(jiǎn)單講是一個(gè)Java servlet,扮演多重角色,路由收到的所有的命令給對(duì)應(yīng)的Webdriver實(shí)例。諸如此類的事情,一個(gè)大二的學(xué)生都能完成。Firefox driver也完成了遠(yuǎn)程Webdriver協(xié)議,它的架構(gòu)更加有趣,所以讓我們跟蹤一個(gè)從語(yǔ)言綁定的會(huì)話到后端一直到返回給用戶的請(qǐng)求。