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