selenium +python之Page Obiect設計模式


PageObject是selenium自動化測試項目開發實踐的最佳設計模式之一,它主要體現對界面交互細節的封裝,這樣可以使測試案例更關注於業務而非界面細節,從而提高測試案例的可讀性。

1.認識PageObject

PageObject設計模式的優點如下:

*減少代碼的重復

*提高測試用例的可讀性

*提高測試用例的可維護性,特別針對UI頻繁變動的項目。

當為web頁面編寫測試是,需要操作該web頁面上的元素。然而,如果在測試代碼中直接操作HTML元素,代碼是及其脆弱的,因為ui的變動性會很大。我們可以將page對象封裝成一個HTML頁面,然后通過提供應用程序特定的API來操作頁面元素。而不是在HTML中來定位。

page對象的一個基本經驗法則是:凡是人能夠做的事,page對象通過軟件客戶端都能夠做到。因此,他應該提供一個易於編程的接口並隱藏窗口底層的部件。所以訪問一個文本框應該通過一個訪問方法(accessor method)來實現字符串的獲取與返回,復選框應當使用布爾值,按鈕應當被表示為行為導向的方法名。page對象應當將在GUI控件上所有查詢和操作數據的行為封裝為方法

一個好的經驗法則是:即使改變具體的控件,page對象的接口也不應當發生改變

盡管該術語是:“頁面”對象,但是並不意味着需要針對每個頁面建立一個這樣的對象。例如:頁面有重要意義的元素可以獨立為一個page對象。經驗法則的目的是通過給頁面建模,時期對應用程序的使用者變得更有意義。

 1 from selenium import webdriver
 2 from selenium.webdriver.common.by import By
 3 from time import sleep
 4 
 5 
 6 class Page(object):
 7     """
 8     基礎類,用於頁面對象類的繼承
 9     """
10     login_url = 'http://mail.163.com/'
11 
12     def __init__(self, selenium_driver, base_url=login_url):
13         self.base_url = base_url
14         self.driver = selenium_driver
15         self.timeout = 30
16 
17     def on_page(self):
18         return self.driver.current_url == (self.base_url + self.url)
19 
20     def _open(self, url):
21         url = self.base_url + url
22         self.driver.get(url)
23         assert self.on_page(), 'Did not land on %s' % url
24 
25     def open(self):
26         self._open(self.url)
27 
28     def find_element(self, *loc):
29         return self.driver.find_element(*loc)
30 
31 
32 class LoginPage(Page):
33     """
34     126郵箱登陸頁面模型
35     """
36     url = '/'
37     # 定位器
38     username_loc = (By.ID, "idInput")
39     password_loc = (By.ID, "pwdInput")
40     submit_loc = (By.ID, "loginBtn")
41 
42     # Action
43     def type_username(self, username):
44         self.find_element(*self.username_loc).send_keys(username)
45 
46     def type_password(self, password):
47         self.find_element(*self.password_loc).send_keys(password)
48 
49     def submit(self):
50         self.find_element(*self.submit_loc).click()
51 
52 
53 def test_user_login(driver, username, password):
54     """
55      測試獲取的用戶名/密碼是否可以登陸
56      """
57     login_page = LoginPage(driver)
58     login_page.open()
59     login_page.type_username(username)
60     login_page.type_password(password)
61     login_page.submit()
62 
63 
64 def main():
65     try:
66         driver = webdriver.Chrome()
67         username = 'fengyiru6369@163.com'
68         password = '********'
69         test_user_login(driver, username, password)
70         sleep(3)
71         text = driver.find_element_by_xpath("//span[@id = 'spnUid']").text
72         assert (text == 'fengyiru6369@163.com'), "用戶名稱不匹配,登陸失敗!"
73     finally:
74         # 關閉瀏覽器窗口
75         driver.close()
76 
77 
78 if __name__ == '__main__':
79     main()

 代碼解釋如下:

1、創建page類

 1 class Page(object):
 2     """
 3     基礎類,用於頁面對象類的繼承
 4     """
 5     login_url = 'http://mail.163.com/'
 6 
 7     def __init__(self, selenium_driver, base_url=login_url):
 8         self.base_url = base_url
 9         self.driver = selenium_driver
10         self.timeout = 30
11 
12     def on_page(self):
13         return self.driver.current_url == (self.base_url + self.url)
14 
15     def _open(self, url):
16         url = self.base_url + url
17         self.driver.get(url)
18         assert self.on_page(), 'Did not land on %s' % url
19 
20     def open(self):
21         self._open(self.url)
22 
23     def find_element(self, *loc):
24         return self.driver.find_element(*loc)
首先創建一個基礎類Page,在初始化方法__init__()中定義驅動(driver),基本的URL(base_url)和超時時間(timeout)等,定義open()方法用於打開URL網站,但是它本身並未做這件事情,而是交由
_open()方法來實現。關於Url地址的斷言部分,則交由on_page()方法來實現,而find_element()方法用於元素的定位。

2.創建LoginPage類

Page類中定義的這些方法都是頁面操作的基本方法。下面根據登陸頁的特點再創建LoginPage類並繼承page類,這也是page object設計模式中最重要的對象層。

 1 class LoginPage(Page):
 2     """
 3     126郵箱登陸頁面模型
 4     """
 5     url = '/'
 6     # 定位器
 7     username_loc = (By.ID, "idInput")
 8     password_loc = (By.ID, "pwdInput")
 9     submit_loc = (By.ID, "loginBtn")
10 
11     # Action
12     def type_username(self, username):
13         self.find_element(*self.username_loc).send_keys(username)
14 
15     def type_password(self, password):
16         self.find_element(*self.password_loc).send_keys(password)
17 
18     def submit(self):
19         self.find_element(*self.submit_loc).click()
LoginPage類中主要對登陸頁面上的元素進行封裝,使其成為更具體的操作方法。例如,用戶名,密碼和登陸按鈕都被封裝成了方法。

3.創建test_user_login()函數

1 def test_user_login(driver, username, password):
2     """
3      測試獲取的用戶名/密碼是否可以登陸
4      """
5     login_page = LoginPage(driver)
6     login_page.open()
7     login_page.type_username(username)
8     login_page.type_password(password)
9     login_page.submit()

test_user_login()函數將單個的元素操作組成一個完整的動作,而這個動作包含了打開瀏覽器,輸入用戶名/密碼,點擊登陸等單步操作。在使用該函數時需要將driver,username、password等信息作為函數的入參,這樣該函數具有很強的可重用性。

4.創建main()函數

 1 def main():
 2     try:
 3         driver = webdriver.Chrome()
 4         username = 'fengyiru6369@163.com'
 5         password = '********'
 6         test_user_login(driver, username, password)
 7         sleep(3)
 8         text = driver.find_element_by_xpath("//span[@id = 'spnUid']").text
 9         assert (text == 'fengyiru6369@163.com'), "用戶名稱不匹配,登陸失敗!"
10     finally:
11         # 關閉瀏覽器窗口
12         driver.close()
13 
14 
15 if __name__ == '__main__':
16     main()

main()函數更接近於用戶的操作行為,對用戶來說,要進行郵箱登陸,需要關心的就是通過哪個瀏覽器打開郵箱網址,登陸的用戶名和密碼是什么,至於輸入框,按鈕時如何定位的,則不需要關心。

這樣分層的好處是,不同的層關心不同的問題。頁面對象層只關心元素定位的問題,測試用例只關心測試數據。

一個有分歧的地方是page對象是否應自身包含斷言,或者僅僅提供數據給測試腳本來設置斷言。在page對象中包含斷言的倡導者認為,這又助於避免在測試腳本中出現重復的斷言,可以更容易的提供更好的錯誤信息,並且提供更接近只做不問風格的API。不在page對象中包含斷言的倡導者認為,包含斷言會混合訪問頁面數據和實現斷言邏輯的職責,並且導致page對象過於臃腫。

在page對象中不包含斷言,雖然我們可以通過為常用的斷言提供斷言庫的方式來消除重復,提供更好的診斷,但從用戶的角度去自動化的觀點來看,判斷是否登陸成功是用戶需要做的事情,不該交由頁面對象層來完成。

使用page object模式之后的另外一個好處就是有助於降低冗余。如果需要在10個用例中輸入不同的用戶名/密碼登陸,那么main()方法竟會變得非常簡潔。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM