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()里就調用登錄的模塊腳本。同樣,你寫登錄的事件,可能封裝了瀏覽器的調用。具體問題要具體分析,實際腳本開發過程要隨機應變,一種方法實現起來困難,就想辦法繞過去,這個是自動化測試工程師要一直面臨的挑戰。
好了,今天的分享就到這里吧!!!謝謝各位的耐心閱讀。有問題加群交流討論
您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波 推薦 不要忘記哦!!!
別忘了點 推薦 留下您來過的痕跡