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

1. 簡介

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

2. unittest單元測試框架

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

       unittest是一個單元測試框架,是Python編程的單元測試框架。有時候,也作叫作「PyUnit」,是Junit的Python語言版本。這裏瞭解下,Junit是Java語言的單元測試框架,Java還有一個很好用的單元測試框架叫TestNG,本系列只學習Python,因此只須要unittest是python

Python裏的一個單元測試框架就能夠了。web

      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.小結

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

 

您的確定就是我進步的動力。若是你感受還不錯,就請鼓勵一下吧!記得隨手點波 推薦 不要忘記哦!!!

別忘了點推薦留下您來過的痕跡

 

原文出處:https://www.cnblogs.com/du-hong/p/12058673.html

相關文章
相關標籤/搜索