上一篇文章介紹了數據源的概念,而且用Excel演示了一個數據驅動的案例。這篇我們給它改進一下,沒打開項目的朋友先把它打開,然后再仔細觀察一下:
注意劃紅線的地方。這段代碼里定位了用戶名、密碼、登錄等等好幾個元素。大家都知道,好多網站一開始挺丑的,在開發人員不斷地修改下才越來越美觀。尤其是那些不用模板的,有可能一開始看起來很不起眼,就幾篇文章配上一堆圖片外加幾個大按鈕就完事了。但一番更改之后的最后成品就不一樣了,比如加了一些套件或是框架后,元素改了樣子,控件挪了位置,最后的成品與一開始的感覺或許截然不同。像我們這個示例網站,如果我寫了許多許多的test case,每一個case都有用戶名、密碼、登錄,有一天我嫌網頁太丑了想改它們的控件樣式和位置,總之代碼有變動需要重新定位。那我是不是需要在每一個case中都改一遍?顯然太費力了。
所以,我們需要把這些頁面上的元素位置放到一個文件里統一管理,然后在不同的test case里引用,這樣未來一旦有變化我改一處就行了。我們知道,這些頁面上的元素或是控件本身聲明出來都是WebElement類型的對象,也就是driver.findElement()返回的東西。之前從來沒詳細討論過括號里的內容,只知道它是定位一個元素/控件的過程,以By調用定位器來表示:
其實括號里面也可以單獨拿出來寫,調用之后返回的是一個By類型的對象,這個對象承載著該元素定位的所有信息,測試界里又管它叫object。
注意,這個object特指這個By類型的對象,跟我們之前學java面向對象泛泛而談的對象不一樣。定位器括號里的內容大家都知道,是元素位置字符串,我們要放進文件統一管理的也就是它們。又因為一個object就是依托于該元素位置產生的,所以我又管它叫作object定位符。
理清這些概念之后繼續。object定位符也不是隨隨便便寫到一個文件中的,一般來說我們按照兩種方式來給object定位符歸類 - 第一種,按照元素所在的頁面分類: Test case涉及多少頁面就創建多少個文件,再在每一個文件里寫入所有跟該頁面有關的object定位符;第二種,按照元素所屬的模塊分類: 一個測試網站或許會有許多模塊,比如登錄、留言板、購物車,一個元素屬于哪個模塊你就給它放到對應的文件中去。這種方式文件量或許會少一點;舉個例子,退出按鈕應該是屬于登錄模塊的,但出現的位置卻不是登錄頁面。我所知道的是大多數人都習慣于第一種方式,簡單直接,也有不少人覺得第二種方式既能創建較少的文件,也便于一個被測功能的維護。
按照這個思路,我們在該項目中創建一個新的包叫com.objrepository用來存所有的object定位符文件。這里我按模塊分:由于目前我們只針對登錄模塊,所以在包里新建一個文件叫loginPage.properties(右鍵點擊com.objrepository -> New -> File):
之前介紹java時討論過properties類型文件,它的特點是可以通過一個“=”號把左右兩邊連接起來。這里正好,可以左邊是object定位符的名稱,右邊是它的值,一一對應。對照著項目中出現的object把它們一個一個寫進去,不要遺漏:
注意,給object定位符起名字雖然沒有什么硬性規定,但一個復雜網站需要的測試用例往往既多又雜,里面包括的元素、控件更是數不勝數,一個名字起錯了或是混淆了找起來是非常困難的,畢竟元素種類就那么多,可元素本身卻千變萬化。所以,寫項目時一定要注意提前規定一些格式,包括給object命名這種看起來很小的問題。我喜歡的一種格式是"object類型_object用途"。類型指的是這個object究竟是哪種元素,是textbox呢還是button?用途指的是這個object究竟是干什么用的,功能是什么。大家可以參考一下這個格式,用自己習慣的格式也可以。公司里一般都會給項目定好類似的規范,不管怎么樣,命名格式一定要統一、辨識度高,而且定了就不要變了。
改進后的代碼如下:
第30行聲明一個Properties類的對象引用prop并在第65行實例化,這個時候第64行fis指向的對象也需要改變了,因為文件從Login.xlsx變成了loginPage.properties。劃紅線部分就是通過prop調用getProperty()方法進行取值,注意不要寫錯,寫的是properties文件中等號左邊的字符串。執行一遍:
這樣,以后只要想改變某個元素的定位,我們只要去修改它的object文件即可,不用再去動test case。試想如果有很多個test case,這么做就會大大節省時間和精力。
還沒完,接著利用剛才所講的幾個概念改得再徹底一點,把該模塊上的object、具體的測試步驟和驗證過程都封裝在方法里,然后單獨放到一個類里。再創建一個新的包叫com.pageobjects -> 添加新的類LoginPage.java,項目目錄變成:
先在LoginPage.java中寫入如下代碼:
看畫紅框部分,我通過讀取Login模塊的object定位符率先得到object,然后用在以下三個部分 - 1.頁面元素/object定位與操作; 2.具體測試步驟; 3.驗證過程:頁面元素/object定位與操作相關的方法是enterUserName(),enterPassword(),clickLogin(),clickLogout()等等,它們不光通過driver調用findElement()定位了元素,同樣執行了sendKeys()或click()操作,從方法名也能看出來它們是干什么的,不細講了;具體測試步驟相關的方法是login(),它里面用this關鍵字完成了登錄所需的每一步;驗證過程相關的方法是verifyMenuHomeText()和verifyLoginBtnExist(),分別用來驗證登錄后目錄上的Home按鈕和點擊退出后的登錄按鈕。
除此之外,driver實例從Test.java被傳遞進來用于定位,而且從LoginPage.java文件名也可以看出來,它只裝和Login模塊有關的東西,別的模塊的不要亂入。最后修改Test.java:
之前的一大堆代碼全部變成了紅框里的一小部分,所有的測試步驟和驗證全部交給LoginPage.java去做,大大簡化了主類。所以,有object參與的數據驅動測試程序擴展性更強,也更加便于維護。回到剛才說的,我們可以用頁面和模塊兩種方法給object歸類,多數人用頁面的方法,頁面的英文又是page,所以測試界管這種程序結構叫Page Object Model(POM),翻譯過來就是頁面對象模型,我以后就簡稱POM了。雖然"Page"這個詞是頁面的意思,但我覺得"Page Object"里面"Page"不一定非要拘泥于“頁面”,也可以是“模塊”的意思,比如我剛才的演示,不是挺好的嘛。不管用哪種方法,POM是一種把元素定位、測試步驟和驗證過程單獨封裝的模型,其核心就是把object與test case分離,單獨管理。總結一下,剛剛我們架構的一個簡單POM做了這兩步:
當然,實現POM并不是這一種格式,有人說是不是必須用properties文件?能不能用別的文件?當然可以,只要object單獨存放就可以。有人說是不是元素定位、測試步驟和驗證過程三者缺一不可,都要寫到單獨的一個類中?再細分成三個類行不行?沒問題啊,只要你看著清楚不亂就行。總之,一個宗旨就是POM是一種把元素定位、測試步驟和驗證過程單獨封裝的模型。只要不違背這個宗旨,你可以隨意架構你的程序。
這篇文章的源代碼在SeleniumExcelDataDrivenPOM項目里邊。
本篇知識點及注意事項:
1. POM是一種把元素定位、測試步驟和驗證過程單獨封裝的模型,其核心就是把object與test case分離,單獨管理。
2. 實現自動化測試POM架構的一種方法是先把元素及其定位放入properties文件中,再把元素定位、測試步驟和驗證過程單獨寫到一個類中。