1. 程式人生 > >Python+Selenium框架設計篇之7-進一步實現POM和可能遇到問題解決辦法

Python+Selenium框架設計篇之7-進一步實現POM和可能遇到問題解決辦法

       本文進一步演示POM的具體實現,前面POM只是一個頁面,一個測試指令碼,現在我們要實現三個頁面,兩個測試指令碼。在pageobjects包下,我新建了2個頁面物件:百度新聞首頁,百度體育新聞首頁,具體檔案結構如下圖,其他和之前專案層級結構保持不變。


百度首頁頁面類程式碼(baidu_homepage.py),定義了百度新聞的入口

# coding=utf-8
from 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']"

    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)
百度新聞首頁的頁面類程式碼(baidu_news_home.py),定義了體育新聞入口
# coding=utf-8
from framework.base_page import BasePage


class NewsHomePage(BasePage):
    # 點選體育新聞入口
    sports_link = "xpath=>//*[@id='channel-all']/div/ul/li[5]/a"

    def click_sports(self):
        self.click(self.sports_link)
        self.sleep(2)
百度體育新聞頁面類程式碼(news_sports_home.py)
# coding=utf-8
from framework.base_page import BasePage


class SportNewsHomePage(BasePage):
    # NBA入口
    nba_link = "xpath=>//*[@id='channel-submenu']/div/span[2]/a[1]"

    def click_nba_link(self):
        self.click(self.nba_link)
        self.sleep(2)
測試類程式碼(test_nba_news_view.py)

       測試步驟大概是:百度首頁點選新聞連結-進入新聞主頁,點選體育-進入體育新聞主頁,點選NBA-進入NBA頁面-其他後續指令碼操作。為什麼要採用這樣的步驟呢,幹嘛不直接driver.get('nba的連結')?因為我們就是要利用POM的思想去寫我們測試指令碼,才有上面的測試步驟。

# coding=utf-8
import time
import unittest
from framework.browser_engine import BrowserEngine
from pageobjects.baidu_homepage import HomePage
from pageobjects.baidu_news_home import NewsHomePage
from pageobjects.news_sport_home import SportNewsHomePage


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 = SportNewsHomePage(self.driver)
        sportnewhome.click_nba_link()
        sportnewhome.get_windows_img()

if __name__ == '__main__':
    unittest.main()
通過上面的

指令碼,進入一個新的頁面,就要初始化這個頁面的物件,然後才能呼叫這個頁面相關的方法,driver這個例項物件在不同頁面之間切換,這個就是POM的核心內容。我們來測試執行這個類看看,結果報錯。

StaleElementReferenceException: Message: stale element reference: element is not attached to the page document

原因分析:

字面意思是說,頁面元素不在當前頁面物件沒有載入到頁面,就不能找到元素,不能進行點選,這個報錯發生在,百度新聞首頁點選體育這行程式碼裡。

由於我們的driver這個例項物件在不同的頁面裡切換,可能造成了這個報錯,這個問題在python+selenium遇到過,java+selenium沒有遇到,國外網站,有人建議,既然找不到這個元素,那麼在腳本里,就直接driver.find_elemen(xpath)再找一次。也就是說,可能我們利用頁面物件方法,點選不了這個體育連結,那麼我們直接在腳本里通過find_element方法去定位體育這個元素,然後再點選。這個也算是一個bug,目前暫時沒有更好辦法解決,不知道以後chromedriver.exe升級會不會解決這個問題不好說。

我們調整下我們測試類程式碼,新增find_element()語句

# coding=utf-8
import time
import unittest
from framework.browser_engine import BrowserEngine
from pageobjects.baidu_homepage import HomePage
from pageobjects.baidu_news_home import NewsHomePage
from pageobjects.news_sport_home import SportNewsHomePage


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)
        #self.driver.refresh()
        #newshome.click_sports()
        self.driver.find_element_by_xpath("//*[@id='channel-all']/div/ul/li[5]/a").click()
        #初始化一個體育新聞主頁,點選NBA
        sportnewhome = SportNewsHomePage(self.driver)
        #sportnewhome.click_nba_link()
        self.driver.find_element_by_xpath("//*[@id='channel-submenu']/div/span[2]/a[1]").click()
        sportnewhome.get_windows_img()

if __name__ == '__main__':
    unittest.main()
       其實,我們之前頁面物件呼叫點選相關元素進入下一個頁面,在回放指令碼是看起作用了,但是就是報錯,所以這裡,只好在三個地方點選進入下一個頁面的時候,採用self.driver.find_element()方法。這個和我們POM的思想,頁面物件只寫元素定位和相關方法,指令碼類一般不寫頁面元素定位相矛盾,是吧。也許未來能解決這個問題,或者你接受當前這個方法,或者,你單獨寫一個進入到NBA的類,例如直接driver.get()然後封裝靜態類,當做其他NBA頁面指令碼的測試韌體引入,這樣也可以。

       實際專案指令碼開發也應該有一些公共方法封裝成模組或者靜態類,例如,把登入事件寫成靜態類,第二個用例是收藏一篇文章,收藏的測試前提就是登入,所以在收藏類的測試韌體中的setUp()裡就呼叫登入的模組指令碼。同樣,你寫登入的事件,可能封裝了瀏覽器的呼叫。具體問題要具體分析,實際指令碼開發過程要隨機應變,一種方法實現起來困難,就想辦法繞過去,這個是自動化測試工程師要一直面臨的挑戰。