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