《一頭扎進》系列之Python+Selenium框架設計篇2- 價值好幾K的框架,不看白不看,看了還想看

  • 2019 年 12 月 20 日
  • 筆記

1. 簡介

  上一篇介紹了自動化框架的架構,今天宏哥就帶領小夥伴或者童鞋們開始開工往這個框架里開始添磚加瓦。主要是介紹一個框架unittest單元測試框架和一種設計思想POM。

2. unittest單元測試框架

前面文章已經簡單介紹了一些關於自動化測試框架的介紹,知道了什麼是自動化測試框架,主要有哪些特點,基本組成部分等。在繼續介紹框架設計之前,我們先來學習一個工具,叫unittest。

unittest是一個單元測試框架,是Python編程的單元測試框架。有時候,也做叫做「PyUnit」,是Junit的Python語言版本。這裡了解下,Junit是Java語言的單元測試框架,Java還有一個很好用的單元測試框架叫TestNG,本系列只學習Python,所以只需要unittest是

Python里的一個單元測試框架就可以了。

unittest支援測試自動化,共享測試用例中的初始化和關閉退出程式碼,在unittest中最小單元是test,也就是一個測試用例。要了解unittest單元測試框架,先來了解以下幾個重要的概念。

2.1 測試韌體(test fixture)

一個測試韌體包括兩部分,執行測試程式碼之前的準備部分和測試結束之後的清掃程式碼。這兩部分一般用函數setUp()和tearDown()表示。這裡舉例以下,例如要測試百度搜索selenium這個場景,我們的測試韌體可以這樣寫,setUp()里寫打開瀏覽器,瀏覽器最大

化,和打開百度首頁等腳本程式碼;在tearDown()里寫結束搜索後,退出並關閉瀏覽器的程式碼。

2.2 測試用例(test case)

unittest中管理的最小單元是測試用例,一個測試用例,包括測試韌體,和具體測試業務的函數或者方法。一個測試用例中,測試韌體可以不寫,但是至少有一個已test開頭的函數。unittest會自動化識別test開頭的函數是測試程式碼,如果你寫的函數不是test開頭,

unittest是不會執行這個函數裡面的腳本的,這個千萬要記住,所有的測試函數都要test開頭,記住是小寫的哦。

2.3 測試套件 (test suite)

很簡單,就是很多測試用例的集合,叫測試套件,一個測試套件可以隨意管理多個測試用例。如果測試用例比作單個學生,測試套件就是好像是班級的概念。

2.4 測試執行器 (test runner)

test runner是一個用來執行載入測試用例,並執行用例,且提供測試輸出的一個組建。test runner可以載入test case或者test suite進行執行測試任務。

我們舉例來,練習一下test fixture和test case的使用,學習unittest的簡單用法:

2.5 設計思路

1. 新建一個testbaidu.py的文件

2. 導入unittest模組

3. 當前測試類繼承unittest.TestCase,相當於當前利用unittest創建了一個test case,這個test case是能夠被unittest直接識別。

4. 寫setUP(),主要是打開瀏覽器和打開站點

5. 寫一個test_search()用例寫搜索的程式碼

6. 寫tearDown(),主要是瀏覽器退出操作

相關腳本程式碼如下:

2.5.1 程式碼實現:
2.5.2 參考程式碼:
# coding=utf-8?    # 1.先設置編碼,utf-8可支援中英文,如上,一般放在第一行    # 2.注釋:包括記錄創建時間,創建人,項目名稱。  '''  Created on 2019-12-18  @author: 北京-宏哥   QQ交流群:705269076  Project: 《《一頭扎進》系列之Python+Selenium框架設計篇2- 價值好幾K的框架,不看別後悔,過時不候  '''    # 3.導入模組    import time  import unittest  from selenium import webdriver      class BaiduSearch(unittest.TestCase):        def setUp(self):          """          測試韌體的setUp()的程式碼,主要是測試的前提準備工作          :return:          """          self.driver = webdriver.Chrome()          self.driver.maximize_window()          self.driver.implicitly_wait(8)          self.driver.get("https://www.baidu.com")        def tearDown(self):          """          測試結束後的操作,這裡基本上都是關閉瀏覽器          :return:          """          self.driver.implicitly_wait(8)          self.driver.quit()        def test_baidu_search(self):          """          這裡一定要test開頭,把測試邏輯程式碼封裝到一個test開頭的方法里。          :return:          """          self.driver.find_element_by_id('kw').send_keys('selenium')          time.sleep(1)          self.driver.find_element_by_id('su').click()          time.sleep(3)          try:              assert 'selenium' in self.driver.title              print ('Test Pass.')          except Exception as e:              print ('Test Fail.', format(e))    if __name__ == '__main__':      unittest.main()
2.5.3 運行結果:

運行程式碼後,控制台列印如下圖的結果

解釋: 最後結尾處的unittest.main(),添加這個是支援在cmd,裡面,cd到這個腳本文件所在的目錄,然後python 腳本名.py執行,如果不添加這一段,是無法執行cmd裡面運行腳本的,在PyCharm中,不添加最後一段,也可以通過,右鍵 Run "unittest xxx",來達到執行效果。

3. 什麼是POM(Page Object Model)

  前面我們介紹了Python中的單元測試框架unittest,以後我們所有的測試類文件,都採用unittest來輔助我們進行debug和腳本開發。搞定了debug機制和確定了unittest來進行創建和管理我們的自動化測試腳本,接下來我們來考慮下,框架設計中一種很普遍的設計

思想-POM(Page Object Model)。

3.1 POM是什麼

Page Object Model (POM) 直譯為「頁面對象模型」,這種設計模式旨在為每個待測試的頁面創建一個頁面對象(class),將那些繁瑣的定位操作封裝到這個頁面對象中,只對外提供必要的操作介面。

3.2 POM 有什麼好處

POM 將頁面定位和業務操作分開,分離了測試對象和測試腳本,如果UI更改頁面,測試腳本不需要更改,只需要更改頁面對象中的某些程式碼就可以,提高了可維護性。

POM,中文字母意思是,頁面對象模型,POM是一種最近幾年非常流行的自動化測試模型,或者思想,POM不是一個框架,就是一個解決問題的思想。採用POM的目的,是為了解決前端中UI變化頻繁,從而造成測試自動化腳本維護的成本越來越大。下圖,形

象描述了POM的好處。

從上圖看出,採取了POM設計思路和不採取的區別,左側把測試程式碼和頁面元素都寫在一個類文件,如果需要更改頁面,那麼就要修改頁面元素定位,從而要修改這個類中測試程式碼,這個看起來和混亂。右側,採取POM後,主要的區別就是,把頁面元素和業務

邏輯和測試腳本分離出來到兩個不同類文件。ClassA只寫頁面元素定位,和業務邏輯程式碼操作的封裝,ClassB只寫測試腳本,不關心如何元素定位,只寫調用ClassA的程式碼去覆蓋不同的測試場景。如果前端頁面發生變化,只需要修改ClassA的元素定位,而不需要去

修改ClassB中的測試腳本程式碼。

POM主要有以下優點:

1. 把web ui對象倉庫從測試腳本分離,業務程式碼和測試腳本分離。

2. 每一個頁面對應一個頁面類,頁面的元素寫到這個頁面類中。

3. 頁面類主要包括該頁面的元素定位,和和這些元素相關的業務操作程式碼封裝的方法。

4. 程式碼復用,從而減少測試腳本程式碼量。

5. 層次清晰,同時支援多個編寫自動化腳本開發,例如每個人寫哪幾個頁面,不影響他人。

6. 建議頁面類和業務邏輯方法都給一個有意義的名稱,方便他人快速編寫腳本和維護腳本。

3.3 牛刀小試

比如測試一個登陸頁面:新浪微博 ,執行測試的人員傳遞不同的數據到帳號、密碼框就可以了,而不應該去顧慮:頁面是否已經載入完成?怎樣定位到帳號輸入框?怎樣定位到登陸按鈕等等問題。

這些問題全部交由登陸頁面的「頁面對象」去解決並封裝起來,只提供給測試人員三個介面方法:1.帳號輸入介面、2.密碼輸入介面、3.提交介面。

首先定義一個基本頁面 BasePage類 ,定義基本的頁面操作,提供給其他頁面去繼承,basePage.py 內容如下:

3.3.1 程式碼實現:
3.3.2 參考程式碼:
# coding=utf-8?    # 1.先設置編碼,utf-8可支援中英文,如上,一般放在第一行    # 2.注釋:包括記錄創建時間,創建人,項目名稱。  '''  Created on 2019-12-18  @author: 北京-宏哥   QQ交流群:705269076  Project: 《《一頭扎進》系列之Python+Selenium框架設計篇2- 價值好幾K的框架,不看別後悔,過時不候  '''    # 3.導入模組  from selenium import webdriver  from selenium.webdriver.support.wait import WebDriverWait  from selenium.webdriver.support import expected_conditions as EC      class BasePage(object):      """       基礎頁面,提供給其他頁面繼承       """        def __init__(self, driver, base_url, title):          """         初始化         """          self.driver = driver          self.base_url = base_url          self.title = title          def _open(self, url):          """          私有方法,打開url參數指定的頁面,          並檢查打開是否正確          """          self.driver.get(url)          # 顯式等待10秒,如果打開頁title與預期不符或者超時,拋出異常          WebDriverWait(self.driver, 10).until(EC.title_is(self.title))          def open(self):          """          公共方法,調用私有方法_open()打開鏈接          """          self._open(self.base_url)          def find_element(self, *loc):          """          定位指定元素          """          # 顯式等待元素,超過10秒未找到則拋出超時異常(TimeoutException)          # presence_of_element_located: 不關心元素是否可見,只關心元素是否存在在頁面中          # visibility_of_element_located: 不僅找到元素,並且該元素必須可見          WebDriverWait(self.driver, 15).until(EC.visibility_of_element_located(loc))          return self.driver.find_element(*loc)      if __name__ == '__main__':      driver = webdriver.Chrome()      driver.maximize_window()      page = BasePage(driver, 'https://www.baidu.com/','百度一下,你就知道')      page.open()      driver.quit()
3.3.3 運行結果:

運行程式碼後,控制台列印如下圖的結果

再定義一個 LoginPage類 繼承 BasePage類 ,向外提供測登陸方法。文件命名為 xl_login.py ,內容如下:

3.3.4 程式碼實現:
3.3.5 參考程式碼:
# coding=utf-8?    # 1.先設置編碼,utf-8可支援中英文,如上,一般放在第一行    # 2.注釋:包括記錄創建時間,創建人,項目名稱。  '''  Created on 2019-12-18  @author: 北京-宏哥   QQ交流群:705269076  Project: 《《一頭扎進》系列之Python+Selenium框架設計篇2- 價值好幾K的框架,不看別後悔,過時不候  '''    # 3.導入模組    from selenium import webdriver  from selenium.webdriver.common.by import By  from basePage import BasePage      class LoginPage(BasePage):      """      新浪微薄登陸頁面      繼承自基礎頁面BasePage     """      # 定位帳號輸入框      username_loc = (By.ID, 'loginname')      # 定位密碼輸入框      password_loc = (By.NAME, 'password')      # 定位登陸按鈕      submit_loc = (By.XPATH, './/*[@id="pl_login_form"]/div/div[3]/div[6]/a')      # 定位提示資訊,如:請輸入驗證碼      # 不要迷信開發者工具提供的Xpath,      # 比如這裡提供的Xpath://*[@id="layer_15582553868501"]/div/p/span[2],      # id是動態的,無法使用,需自行推到Xpath      message_loc = (By.XPATH, '//div[@class="content layer_mini_info"]/p/span[2]')          # 輸入用戶名操作      def type_username(self, username):          self.find_element(*self.username_loc).send_keys(username)          # 輸入密碼操作      def type_password(self, password):          self.find_element(*self.password_loc).send_keys(password)          # 點擊登陸按鈕操作      def submit(self):          self.find_element(*self.submit_loc).click()          # 獲取提示資訊      def get_message(self):          return self.find_element(*self.message_loc).text      if __name__ == '__main__':  # 測試登陸      # 預打開頁面      base_url = 'https://weibo.com/'      # 頁面title      title = '微博-隨時隨地發現新鮮事'      # 準備好待輸入的用戶名和密碼      username = 'haha'      password = 'hehe'        # 打開Chrome瀏覽器      driver = webdriver.Chrome()      driver.maximize_window()      # 登陸頁面初始化      login = LoginPage(driver, base_url, title)      # 打開新浪微博頁      login.open()      # 輸入用戶名      login.type_username(username)      # 輸入密碼      login.type_password(password)      # 點擊登陸      login.submit()      # 列印提示資訊      print(login.get_message())
3.3.6 運行結果:

運行程式碼後,控制台列印如下圖的結果

4.小結

好了,今天的分享就到這裡吧!!!謝謝各位的耐心閱讀。有問題加群交流討論