《一頭扎進》系列之Python+Selenium框架設計篇5 - 價值好幾K的框架,哎呦!這個框架還真有點料啊!!!
1. 簡介
其實,到前面這一篇文章,簡單的Python+Selenium自動化測試框架就已經算實現了。接下來的主要是介紹,unittest管理指令碼,如何如何載入執行指令碼,再就是採用第三方外掛,實現輸出html的測試報告。本文來介紹下,在同一個類中,多個測試函式時候,測試韌體如何寫和進一步實現POM和可能遇到問題解決辦法。
2. 一個類檔案多個測試方法情況下測試韌體的寫法
為了說明這個問題,我們在之前的測試類基礎上,再寫一個test_search2()的測試用例,看看會發生什麼。
2.1 程式碼實現:
2.2 參考程式碼:
# -*- coding:utf-8 -*- # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-12-23 @author: 北京-巨集哥 QQ交流群:705269076 Project: 《《一頭扎進》系列之Python+Selenium框架設計篇5- 價值好幾K的框架,不看別後悔,過時不候 ''' # 3.匯入模組 import time import unittest from automation_framework_demo.framework.browser_engine import BrowserEngine from automation_framework_demo.pageobjects.baidu_homepage import HomePage class BaiduSearch(unittest.TestCase): def setUp(self): """ 測試韌體的setUp()的程式碼,主要是測試的前提準備工作 :return: """ browse = BrowserEngine(self) self.driver = browse.open_browser(self) def tearDown(self): """ 測試結束後的操作,這裡基本上都是關閉瀏覽器 :return: """ self.driver.quit() def test_baidu_search(self): """ 這裡一定要test開頭,把測試邏輯程式碼封裝到一個test開頭的方法裡。 :return: """ homepage = HomePage(self.driver) homepage.type_search('selenium') # 呼叫頁面物件中的方法 homepage.send_submit_btn() # 呼叫頁面物件類中的點選搜尋按鈕方法 time.sleep(2) homepage.get_windows_img() # 呼叫基類截圖方法 try: assert 'selenium' in homepage.get_page_title() # 呼叫頁面物件繼承基類中的獲取頁面標題方法 print('Test Pass.') except Exception as e: print('Test Fail.', format(e)) def test_search2(self): homepage = HomePage(self.driver) homepage.type_search('python') # 呼叫頁面物件中的方法 homepage.send_submit_btn() # 呼叫頁面物件類中的點選搜尋按鈕方法 time.sleep(2) homepage.get_windows_img() # 呼叫基類截圖方法 if __name__ == '__main__': unittest.main()
2.3 執行結果:
執行程式碼後,控制檯列印如下圖的結果
問題發現了沒,我們的瀏覽器啟動和關閉了兩次,是不是這個問題?其實細心地小夥伴或者童鞋們在上一篇文章裡就可能發現這個問題了,由於時間的關係巨集哥在這裡把它單獨拿出來分享講解一下,希望可以加深小夥伴或者童鞋們的印象。問題是原來每執行一次
test開頭的函式,都要執行一次測試韌體,也就是說執行setUp()和()一次,如果有N個test開頭的函式,測試韌體就執行N次,我們到底有沒有,只需要執行一次測試韌體,支援執行多次測試函式。
我們測試中,肯定需要,開啟一個頁面,然後測試這個頁面的多個用例,才關閉這個頁面,去測試其他頁面,在unittest是有相關測試韌體方法去支援這種行為。請看下面調整,自己對比下,能不能找出不同。
2.4 程式碼實現:
2.5 參考程式碼:
# -*- coding:utf-8 -*- # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-12-20 @author: 北京-巨集哥 QQ交流群:705269076 Project: 《《一頭扎進》系列之Python+Selenium框架設計篇4- 價值好幾K的框架,不看別後悔,過時不候 ''' # 3.匯入模組 import time import unittest from automation_framework_demo.framework.browser_engine import BrowserEngine from automation_framework_demo.pageobjects.baidu_homepage import HomePage class BaiduSearch(unittest.TestCase): @classmethod def setUpClass(cls): """ 測試韌體的setUp()的程式碼,主要是測試的前提準備工作 :return: """ browse = BrowserEngine(cls) cls.driver = browse.open_browser(cls) @classmethod def tearDownClass(cls): """ 測試結束後的操作,這裡基本上都是關閉瀏覽器 :return: """ cls.driver.quit() def test_baidu_search(self): """ 這裡一定要test開頭,把測試邏輯程式碼封裝到一個test開頭的方法裡。 :return: """ # self.driver.find_element_by_id('kw').send_keys('selenium') # time.sleep(1) homepage = HomePage(self.driver) homepage.type_search('selenium') # 呼叫頁面物件中的方法 time.sleep(12) homepage.send_submit_btn() # 呼叫頁面物件類中的點選搜尋按鈕方法 time.sleep(12) homepage.get_windows_img() # 呼叫基類截圖方法 print(self.driver.title) try: assert('selenium' in HomePage.get_page_title(self)) print('Test Pass.') except Exception as e: print('Test Fail.', format(e)) def test_search2(self): homepage = HomePage(self.driver) homepage.type_search('python') # 呼叫頁面物件中的方法 homepage.send_submit_btn() # 呼叫頁面物件類中的點選搜尋按鈕方法 time.sleep(2) homepage.get_windows_img() # 呼叫基類截圖方法 if __name__ == '__main__': unittest.main()
2.6 執行結果:
執行程式碼後,控制檯列印如下圖的結果
執行一下,是不是,只需要開啟和關閉瀏覽器一次,就執行了2個搜尋用例?以後,專案中基本採用這種方法來執行同一個功能不同測試用例的編寫。
3. 進一步實現POM
本小節巨集哥將會進一步演示POM的具體實現,前面POM只是一個頁面,一個測試指令碼,現在我們要實現三個頁面,兩個測試指令碼。在pageobjects包下,我新建了2個頁面物件:百度新聞首頁,百度體育新聞首頁,具體檔案結構如下圖,其他和之前專案層級結構保持不變。
1. 百度首頁頁面類程式碼(baidu_homepage.py),定義了百度新聞的入口
3.1 程式碼實現:
3.2 參考程式碼:
# -*- coding:utf-8 -*- # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-12-23 @author: 北京-巨集哥 QQ交流群:705269076 Project: 《《一頭扎進》系列之Python+Selenium框架設計篇5- 價值好幾K的框架,不看別後悔,過時不候 ''' # 3.匯入模組 from automation_framework_demo.framework.base_page import BasePage class HomePage(BasePage): input_box = "id=>kw" search_submit_btn = "xpath=>//*[@id='su']" #百度新聞入口 #news_link = "xpath=>//*[@id='u1']/a[@name='tj_trnews']" news_link = "xpath=>//*[@id='u1']/a[@name='tj_trnews']" def type_search(self, text): self.type(self.input_box, text) def send_submit_btn(self): self.click(self.search_submit_btn) def click_news(self,): self.click(self.news_link) self.sleep(2)
2. 百度新聞首頁的頁面類程式碼(baidu_news_home.py),定義了體育新聞入口
3.3 程式碼實現:
3.4 參考程式碼:
# -*- coding:utf-8 -*- # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-12-23 @author: 北京-巨集哥 QQ交流群:705269076 Project: 《《一頭扎進》系列之Python+Selenium框架設計篇5- 價值好幾K的框架,不看別後悔,過時不候 ''' # 3.匯入模組 from automation_framework_demo.framework.base_page import BasePage class NewsHomePage(BasePage): #點選體育新聞入口 sports_link = "xpath=>//*[@id='channle-all']/div/ul/li[7]/a" def click_sports(self): self.click(self.sports_link) self.sleep(2)
3. 百度體育新聞頁面類程式碼(news_sports_home.py)
3.5 程式碼實現:
3.6 參考程式碼:
# -*- coding:utf-8 -*- # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-12-23 @author: 北京-巨集哥 QQ交流群:705269076 Project: 《《一頭扎進》系列之Python+Selenium框架設計篇5- 價值好幾K的框架,不看別後悔,過時不候 ''' # 3.匯入模組 from automation_framework_demo.framework.base_page import BasePage class SportsNewsHomePage(BasePage): #NBA入口 nba_link = "xpath=>.//*[@id='col_focus']/div[1]/div[2]/div/div[2]/div/ul/li[1]/a" def click_nba_link(self): self.click(self.nba_link) self.sleep(2)
4. 測試類程式碼(test_nba_news_view.py)
測試步驟大概是:百度首頁點選新聞連結-進入新聞主頁,點選體育-進入體育新聞主頁,點選NBA-進入NBA頁面-其他後續指令碼操作。為什麼要採用這樣的步驟呢,幹嘛不直接driver.get('nba的連結')?因為我們就是要利用POM的思想去寫我們測試指令碼,才有上面的測試步驟。
4.1 程式碼實現:
4.2 參考程式碼:
# -*- coding:utf-8 -*- # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-12-23 @author: 北京-巨集哥 QQ交流群:705269076 Project: 《《一頭扎進》系列之Python+Selenium框架設計篇5- 價值好幾K的框架,不看別後悔,過時不候 ''' # 3.匯入模組 import time import unittest from automation_framework_demo.framework.browser_engine import BrowserEngine from automation_framework_demo.pageobjects.baidu_homepage import HomePage from automation_framework_demo.pageobjects.baidu_news_home import NewsHomePage from automation_framework_demo.pageobjects.news_sport_home import SportsNewsHomePage class ViewNBANews(unittest.TestCase): def setUp(self): browse = BrowserEngine(self) self.driver = browse.open_browser(self) def tearDown(self): self.driver.quit() def test_view_nba_views(self): # 初始化百度首頁,並點選新聞連結 baiduhome = HomePage(self.driver) baiduhome.click_news() # 初始化一個百度新聞主頁物件,點選體育 newshome = NewsHomePage(self.driver) newshome.click_sports() # 初始化一個體育新聞主頁,點選NBA sportnewhome = SportsNewsHomePage(self.driver) sportnewhome.click_nba_link() sportnewhome.get_windows_img() if __name__ == '__main__': unittest.main()
4.3 執行結果:
執行程式碼後,控制檯列印如下圖的結果
5. 小結
5.1 遇到問題
人品好的小夥伴或者童鞋們或許不會遇到下面的問題,反之則會遇到下面的問題。通過上面的指令碼,進入一個新的頁面,就要初始化這個頁面的物件,然後才能呼叫這個頁面相關的方法,driver這個例項物件在不同頁面之間切換,這個就是POM的核心內容。我們來測試執行這個類看看,結果報錯。
StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
5.2 原因分析:
字面意思是說,頁面元素不在當前頁面物件沒有載入到頁面,就不能找到元素,不能進行點選,這個報錯發生在,百度新聞首頁點選體育這行程式碼裡。
由於我們的driver這個例項物件在不同的頁面裡切換,可能造成了這個報錯,這個問題在python+selenium遇到過,java+selenium沒有遇到,國外網站,有人建議,既然找不到這個元素,那麼在腳本里,就直接driver.find_elemen(xpath)再找一次。也就是說,可能我們
利用頁面物件方法,點選不了這個體育連結,那麼我們直接在腳本里通過find_element方法去定位體育這個元素,然後再點選。這個也算是一個bug,目前暫時沒有更好辦法解決,不知道以後chromedriver.exe升級會不會解決這個問題不好說。
我們調整下我們測試類程式碼,新增find_element()語句
5.3 參考程式碼
# -*- coding:utf-8 -*- # 1.先設定編碼,utf-8可支援中英文,如上,一般放在第一行 # 2.註釋:包括記錄建立時間,建立人,專案名稱。 ''' Created on 2019-12-23 @author: 北京-巨集哥 QQ交流群:705269076 Project: 《《一頭扎進》系列之Python+Selenium框架設計篇5- 價值好幾K的框架,不看別後悔,過時不候 ''' # 3.匯入模組 import time import unittest from automation_framework_demo.framework.browser_engine import BrowserEngine from automation_framework_demo.pageobjects.baidu_homepage import HomePage from automation_framework_demo.pageobjects.baidu_news_home import NewsHomePage from automation_framework_demo.pageobjects.news_sport_home import SportsNewsHomePage class ViewNBANews(unittest.TestCase): def setUp(self): browse = BrowserEngine(self) self.driver = browse.open_browser(self) def tearDown(self): self.driver.quit() def test_view_nba_views(self): # 初始化百度首頁,並點選新聞連結 baiduhome = HomePage(self.driver) # baiduhome.click_news() self.driver.find_element_by_xpath("//*[@id='u1']/a[@name='tj_trnews']").click() # 初始化一個百度新聞主頁物件,點選體育 newshome = NewsHomePage(self.driver) # newshome.click_sports() self.driver.find_element_by_xpath("//*[@id='channel-all']/div/ul/li[7]/a").click() # 初始化一個體育新聞主頁,點選NBA sportnewhome = SportsNewsHomePage(self.driver) # sportnewhome.click_nba_link() self.driver.find_element_by_xpath(".//*[@id='col_focus']/div[1]/div[2]/div/div[2]/div/ul/li[1]/a").click() sportnewhome.get_windows_img() if __name__ == '__main__': unittest.main()
其實,我們之前頁面物件呼叫點選相關元素進入下一個頁面,在回放指令碼是看起作用了,但是就是報錯,所以這裡,只好在三個地方點選進入下一個頁面的時候,採用self.driver.find_element()方法。這個和我們POM的思想,頁面物件只寫元素定位和相關方法,指令碼類一般不寫頁面元素定位相矛盾,是吧。也許未來能解決這個問題,或者你接受當前這個方法,或者,你單獨寫一個進入到NBA的類,例如直接driver.get()然後封裝靜態類,當做其他NBA頁面指令碼的測試韌體引入,這樣也可以。
實際專案指令碼開發也應該有一些公共方法封裝成模組或者靜態類,例如,把登入事件寫成靜態類,第二個用例是收藏一篇文章,收藏的測試前提就是登入,所以在收藏類的測試韌體中的setUp()裡就呼叫登入的模組指令碼。同樣,你寫登入的事件,可能封裝了瀏覽器的呼叫。具體問題要具體分析,實際指令碼開發過程要隨機應變,一種方法實現起來困難,就想辦法繞過去,這個是自動化測試工程師要一直面臨的挑戰。
好了,今天的分享就到這裡吧!!!謝謝各位的耐心閱讀。有問題加群交流討論
您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波 推薦 不要忘記哦!!!
別忘了點 推薦 留下您來過的痕跡