對於作web端自動化測試的人來講,可能接觸selenium比QTP還要多,可是咱們在作基於selenium的二次開發的時候,常常會說到二次開發是爲了易於維護,不少人可能不懂得維護的價值是什麼,和到底要維護什麼。今天專門寫一篇關於二次開發的文章,但願可以幫到有須要作二次開發的人。
二次開發也就是咱們常說的封裝selenium,或者作框架。可是一個框架要包含豐富的類和方法。要有一套完整的體系來幫助咱們進行封裝。能夠說框架的設計思想就是整個框架的靈魂,若是設計思想很正確也就意味着這個框架成功了一半,剩下的就是咱們怎麼樣用程序實現這個思想,在開發的過程當中咱們也許會用到一些設計模式和引用一些開源框架。這些只是一個開發人員或者程序設計者的基本素質。至於若是把selenium可以有效的封裝和一些基本思想,咱們來詳細的瞭解一下。
在這篇文章裏面只針對selenium的webdriver來進行討論,咱們再也不對rc作任何的解釋和說明。咱們都知道webdriver的使用過程當中,貫穿始終的就是一個driver, 而且這個driver表明了一個瀏覽器的當前窗口,咱們進行操做的過程當中只是進行當前窗口的操做,也就是最這個current window進行的一系列的操做,若是咱們須要對打開的新的window來進行操做的話,咱們須要switchTo,包括操做frame,固然整個流程下的操做確實讓咱們以爲不是很難編寫,可是咱們編寫腳本的過程當中須要用到的一些輔助功能可能就會很難的編寫,好比最大化瀏覽器,視角移動到操做的元素等等,這個過程一次編寫咱們能夠作到,可是反覆的編寫的話確定是一個讓人很頭疼的過程,因此這個時候咱們要去封裝一些經常使用的方法,咱們有了作一個比較完整的框架的想法,可是咱們突然又意識到了,這樣的話,咱們須要把driver封裝起來,由於整個測試的case都是針對的這個driver,而且只有一個driver,這樣子的話咱們不容許創造多個的driver,也就意味着咱們要把本身編寫的小工具類和driver聯繫起來,而且咱們的測試用例case類也須要調用這個driver,其實很簡單,咱們能夠用注入的方式來作,把driver當成tools類的一個屬性值,而後注入到咱們的case類中,也能夠經過set的方法來進行操做。有了這些基礎,咱們能夠防止無限的編寫重複的方法,這樣咱們有了本身的工具類。若是說這就是框架的話,就會顯得很是的膚淺,由於咱們寫的這些方法根本沒有任何邏輯可言,只是把須要的方法通通的堆到了一塊兒,因此這個時候咱們須要想一想用到某些方法來進行分一下層次。
Page類和Window類:
PageObject模式咱們都知道,就是把資源都放入到page類裏面,而後再編寫邏輯類。這樣的話就能夠無限的複用這些資源,這只是籠統的講了一下設計的思想,至於PageObject到底怎麼去實現這些設計呢?咱們從webDriver的使用開始入手。webdriver是先從定義瀏覽器開始的。WebDriver driver = new FirefoxDriver(); 這樣咱們就定義了一個firefox的瀏覽器,可是自動化的過程不可能只容許咱們把定義瀏覽器的操做放在框架代碼裏面,那樣的硬編碼方式使咱們的case不存在可移植性了,若是進行兼容性測試的話,維護起來對這些case的修改量是比較大的,這種硬編碼方式是咱們不可以進行大量維護的,因此咱們須要把定義瀏覽器的過程徹底放在case類裏面,就是在咱們寫測試用例的時候再去編寫到底用什麼瀏覽器,防止在編寫框架的時候硬編碼的形式把瀏覽器寫死在了框架裏面。作到多瀏覽器的可維護性,對於咱們進行兼容測試也有必定的幫助,這樣的話咱們須要對瀏覽器的選擇部分要進行必定的編碼設計,來完成瀏覽器的可選擇性。在咱們定義完了瀏覽器以後,這個時候咱們也許以爲就是開始查找元素了,可是在這個driver的基礎上咱們應該發現其實這個時候driver表明的整個頁面的操做。可是在頁面的操做基礎上咱們應該意識到還有一個級別的操做,那就是window的操做,就是針對瀏覽器自身的操做。包括一些基本的返回,向前,最大化,最小化,或者移動到制定元素的位置,調用js等等等,這些方法的級別是出於window級別的,和頁面無關的。因此咱們應該把這些全部的方法都封裝到單獨的一個層次中,咱們暫且稱之爲window包中,剛纔的瀏覽器的選擇的全部方法咱們放browser包中。這樣咱們設計出了兩個層次。下面的設計該如何進行呢?咱們知道pageObject的思想是把資源都放入到咱們定義的page類裏面,因此這個時候咱們須要思考了,咱們如何設計這裏的page類呢?按照pageObject的思想來看,page類應該是咱們本身編寫的,那樣咱們的框架是否是就能夠放棄編寫page類了呢?直接封裝一些通用的方法?顯然是不對的,對於頁面html源碼操做的過程當中,咱們煩透了這些元素查找的硬編碼方式,在一個case裏面或者兩個case裏面反覆的調用編寫是讓人頭疼的一件事情,因此咱們能夠把全部的單頁面看作一個層次,裏面和PageObject的思想同樣,就是放入了通用的方法,可是它存在的意義不僅是這樣簡單,由於咱們操做的過程當中須要涉及到frame。而且頁面和頁面之間涉及到不一樣window之間的切換,因此如何協調window和page之間的關係成爲了咱們須要注意的難點和重點,咱們知道webdriver裏面有一個方法叫作getWindowHandlers,這個方法能夠得到全部的句柄,咱們想設計這個page類那麼意味着咱們須要去完美的配合window類,他們之間惟一的關聯就是這個方法,因此咱們能夠設計一個概念,叫作頁面集合,在建立window對象的時候咱們就會自動的出現一個頁面集合器,它的功能就是管理全部的在操做過程當中打開的頁面。而且可以指定一個特殊的page,就是當前頁面。也就是webdriver中的driver對象,由於它一直都是針對當前頁面編程的。那樣咱們的window類裏面就存在了兩個屬性,一個收集器,一個當前頁。這樣咱們在window的級別上就可以徹底操做page類了,這樣咱們在case類設計的時候,只須要經過window類的級別進行編碼就能夠了,徹底能夠把page類看成一種資源來處理,咱們全部須要的東西都是經過page來獲取的。page類的設計中咱們必定要編寫通用的方法。好比獲取title等等等,最主要的一點就是要進行frame的處理操做,咱們如何把frame完美的結合在page裏面呢?咱們在普通的webdriver腳本編寫過程當中可能反反覆覆的switchTo的方法讓咱們很惱火,而且很難讓咱們理解這些腳本究竟是想表達什麼意思呢?因此咱們須要進行對page進行層次上的小分級,就是把page類再分爲一個frame的類和一個模塊的類,由於一個大型頁面裏面,通用的結構和模塊是不少的。咱們單獨的定義一個模塊類,可讓這些相同的結構複用在裏面的方法。定義frame類主要是處理把定位到frame的操做從case類中脫離出來,咱們編寫到page類裏面或者frame類裏面,提供一種方式或者方法來進行frame的定位就能夠了,簡單實用,而且簡化case類的編寫,畢竟case類並非由設計者來編寫,儘可能作到最簡化。固然咱們能夠成這整個層次都是page類,放在page包裏面。
Element類:
Element類就是咱們經常使用的driver.findElement()的那種形式,就是元素類,什麼是元素類呢?元素就是咱們須要定位的那些東西,咱們在操做過程當中很難知道一個findElement到底查找的是什麼,由於全部的標籤形式都是經過id或者各類各樣的定位方式來實現的。這個時候咱們須要把各類各樣的findElement都通通的放在一塊兒,形成的腳本難以理解讓後續接手的腳本開發人員可能頭疼不已,因此咱們能夠作一些簡單的加工。固然咱們還要作的一個最大的工做就是元素查找的封裝。Element和Page類如何關聯起來。咱們知道page和webElement的關聯就是一個driver.findElement的方法。這樣就能夠在頁面上查找元素了。因此咱們也在element類中經過這種形式來進行他們之間的關聯。咱們經過在element類中添加定位方式的形式來進行元素定位和page的關聯,咱們只須要在編寫本身的page類的過程當中直接加入element類就能夠了,element類提供了全部的關於element的方法,好比鼠標事件和鍵盤事件,還有更重要的元素定位方法。定位的方法在這裏不作任何的推薦,由於每一個人的思路不一樣,實現的方式也不一樣,我我的比較偏向的作法是作一個xml來進行資源的管理,把全部須要的資源都放入到xml裏面,這樣咱們就能夠進行元素的定位了。而且在後期維護中主要維護xml就能夠進行對整個腳本進行維護了,不須要咱們大量的從新進行源碼的分析和修改了。固然這是設計的優化過程,由於定位的實現咱們仍是須要本身來完成的,咱們知道元素的定位方式各類各樣,咱們怎麼來進行管理和定位呢?咱們能夠經過map的方法做爲屬性值來進行元素的管理,他的各類定位方法存放在map中,咱們須要的時候只須要調一下就能夠了。經過觀察源碼findElement也是經過map的形式來進行元素定位存儲的。咱們能夠借鑑一下源碼的實現方式。固然咱們徹底封裝findElement也是能夠的。說到這裏可能咱們有一個問題比較難以解決,那就是層級定位。若是咱們只是給element類添加定位方式的話,那麼findElement提供的一級一級的定位方式咱們就沒法應用了,因此在element類中咱們必要還要提供findElement的方法進行層級定位。這只是爲了把webdriver的全部方法都儘可能應用到而已。其實經過xpath的方式咱們就能夠基本上定位大多數的元素。剩下的內容就是咱們對element內容的擴充了。咱們能夠把元素的更加具體的抽象出來,好比咱們把select,table,checkbox等等等的各類html標籤元素顯式的定義出來,在咱們定義一個元素的時候咱們可以更加清晰的看到這個元素的含義,咱們知道它是一個按鈕或者table等等等,這些小元素的操做須要咱們本身深刻理解和開發,這裏不作過多的介紹。
其餘類:
經過這些層次的分析咱們已經出現一個框架的雛形了,而後咱們剩下的設計就是基於完善和優化了。在一個自動化過程當中case類是很是重要的,咱們須要知道case類運行結束的結果報告和分析,因此case類的運行等等一系列的東西咱們都得有統計,這些東西必需要咱們提供一些類來實現,不過所幸的是,強大的junit或者testng徹底能夠取代咱們要去作的工做,他們能夠很完美的提供這些功能,咱們只須要介入這些開源包就能夠了。咱們使用QTP的過程當中咱們常常會用到參數化,咱們自動化的設計都有了,可是沒有參數化的功能怎麼辦?咱們應該先想象一下數據提供的方式。testng提供了一種參數化的形式,可是它是須要在xml裏面配置或者硬編碼的形式來進行編寫。不過它提供了一種dataprovider的方式來進行參數化,它傳遞參數的形式是Object[][],咱們可能但願使用參數的時候經過excel表格來完成,這些都是能夠實現的,poi包提供瞭解析excel的功能,很是的強大。咱們能夠本身編寫解析類來進行參數化的功能編寫,具體實現再也不過多去說。調用的方式就簡單多了,硬性的記住幾個註解就能夠了。case類的各類運行咱們都有了,還須要一些什麼擴展呢?很顯然就是日誌的擴展。日誌的設計也是頗有技巧的。咱們須要用日誌監控某一些方法的話,若是前期沒有直接加入日誌功能,咱們能夠經過spring的方式來進行日誌切入操做。可是咱們在觀察日誌的時候咱們一般會但願知道到底運行到哪一步了,咱們能夠想想,全部的操做都是基於element或者window的,page的只是一個抽象出來的概念,因此咱們只須要把日誌加入到每個element的方法和window的主要方法裏面就能夠監控到整個運行的過程,畢竟咱們不可以去親自盯着屏幕一直。這樣沒個方法不外乎就是運行成功和失敗,因此咱們能夠經過這種方式來進行編碼。日誌的實現咱們能夠經過經過的log4j或者本身編寫一個小的日誌系統。都是可行的方案。
擴展類:
也許咱們須要這些系統可以有良好的可移植性,咱們能夠本身編寫類加載器,爲之後作整個自動化的測試平臺作準備。最主要都是咱們作的這些操做可能須要越來簡單,因此咱們可能會由於引入註解的方式來提供編碼效率,因此咱們還須要爲註解類作一些輔助的工做。固然這些註解的開發須要咱們作足足夠的需求研究,並非無謂的去開發各類註解。這些註解的應用應該說很普遍,再也不多說註解的好處。而且註解還有一點可應用的地方就是放在數據庫的操做中,在自動化測試中,其實數據庫的測試也是一個大的難點。在這裏咱們只討論前端自動化的設計,不過多的討論別的東西。
前面講到的這些類的存在形式其實就是在框架裏面的一種層次,咱們談論的這些都是基於webdriver的,而且主要基於前端自動化測試的,固然自動化測試不僅包括這些,還包括服務器端,接口自動化,單元自動化等等。我我的的能力水平也是頗有限,可能不少地方說到的不是很到位,但願可以經過這篇文章可以給那些但願學習自動化,但願編寫小測試框架的童鞋,一點點的啓發。若是真的有須要想了解的更多,能夠留言給我~~互相交流~html