1. 什么是框架
1.1 定義:
框架(Framework)是整個或部分系統的可重用設計,表現為一組抽象構件(類)及構件(類)實例間交互的方法。
1.2 為什么要搭建自動化測試框架
自動化測試的開發,通常是由自動化測試的需求決定的。這個需求主要包括:
-
- 自動化測試更便於實施
- 解決自動化測試腳本本身存在的問題,如異常處理和場景恢復。
- 測試易於維護。好的框架,可以減少你在管理維護中所投入的人力物力精力。
- 可重用性。框架的意義之一就在於可重用吧。所以在框架里,你可以實現一些通用功能,簡化腳本開發過程。
- 美觀易讀的測試報告。拿Selenium來說,它產出的測試報告只是基於測試腳本的,並沒有那種基於測試集的報告,所以如果你要,測試框架里可以實現。
1.3 自動化測試框架
(1)定義:是一個集成體系,在這一體系中包含測試功能的函數庫、測試數據源、測試對象識別標准,以及種可重用的模塊。
(2)經歷階段: 自動化測試框架在發展的過程中經歷了幾個階段,模塊驅動測試、數據驅動測試、對象驅動測試。
2. 什么是設計模式?
2.1 定義
設計模式是軟件開發人員在軟件開發過程中面臨的一般問題的解決方案。這些解決方案是眾多軟件開發人員經過相當長的一段時間的試驗和錯誤總結出來的。
2.2使用設計模式
是為了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。項目中合理地運用設計模式可以完美地解決很多問題,每種模式在現實中都有相應的原理來與之對應,每種模式都描述了一個在我們周圍不斷重復發生的問題,以及該問題的核心解決方案,這也是設計模式能被廣泛應用的原因。
參見網站:http://www.runoob.com/design-pattern/design-pattern-tutorial.html
3. Page Object Model設計模式
3.1 定義
對於簡單的Selenium自動化測試,我們要做的不過是找到頁面元素,並且值傳遞給這些元素。但是假如有10個腳本同時調用了一個相同的頁面元素,當這個元素發生改變,我們需要修改10個腳本。隨着腳本數的增加,時間工作復雜度也飛速增長。這個時候我們就可以考慮設計一個類,專門用來頁面元素的查找、傳遞值和修正。這樣,當一個頁面元素發生改變的時候,只用修改一個類,而不用同時修改10個腳本。
Page Object是一種程序設計模式,將面向過程轉變為面向對象(頁面對象),將測試對象(按鈕、輸入框、標題等)及單個的測試步驟封裝在每個Page對象中,以page為單位進行管理。
這樣,在Selenium測試頁面中可以通過調用頁面類來獲取頁面元素,從而巧妙的避免了當頁面元素id或者位置變化時,需要改測試頁面代碼的情況。當頁面元素id變化時,只需要更改測試頁Class中頁面的屬性即可。可以使代碼復用,降低維護成本,提高程序可讀性和編寫效率。
3.2 優點
- 以頁面為單位,集中管理元素對象和方法。當頁面元素或流程變動時只需要修改相關頁面方法即可,不需要修改相應腳本
- 編寫腳本簡單,順着業務邏輯寫腳本。page object模式以業務邏輯上的每一步操作作為區分點,頁面方法代表了此頁面的一個業務操作並嚴格控制此操作的后續流程
- 后期維護方便
3.3 Page Factory
Page Factory是一個很好的設計Object repository的模式,以此來實現Selenium WebDriver中的POM概念。
4. Page Object的對象
4.1 Page模式:
1. 抽象出一個BasePage基類,包含一個指向selenium.webdriver的屬性
2. 每一個webpage都繼承BasePage,通過driver來獲取本頁面的元素,每個頁面的操作都抽象為一個個方法
3. TestCase繼承unnitest.TestCase類,並依賴相應的Page類來實現相應的test case步驟
4.2 PO對象以及整個項目結構
-
data
- 存放公共數據,例如用戶名和密碼
-
Public
- 設計全局方法和變量
例如:設計一個可以代表項目所在根目錄的全局變量
1 # coding = utf-8 2 import os 3 class global_value(): 4 # 1. 項目所在的根目錄 5 def get_project_dir(): 6 try: 7 project_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 8 print("The project_dir is :", project_dir) 9 return project_dir 10 except: 11 print("Cannot find the project directory") 12 13 PROJECT_DIR = get_project_dir()
如果在其他類中需要這個全局變量,則需要先導入該類,例如在run_test中導入
1 from public import GlobalValue 2 project_dir = GlobalValue.global_value.PROJECT_DIR
-
report
- 存放報告
-
Page 基類(BasePage)
- 設計了一個基本的Page類,以便所有的頁面進行繼承,該類標明了一個sub page類的基本功能和公共的功能。
1 # coding utf-8 2 """ 3 @ author: 宋博文 4 封裝所有的功用方法,例如driver, url, FindElement 5 """ 6 from selenium import webdriver 7 import time 8 import os 9 from selenium.webdriver.support.ui import Select 10 from selenium.webdriver.support.wait import WebDriverWait 11 from selenium.webdriver.common.action_chains import ActionChains 12 from public import GlobalValue 13 project_dir = GlobalValue.global_value.PROJECT_DIR 14 15 class base_page(): 16 17 # 初始化driver、url、pagetitle等 18 # 實例化BasePage類時,最先執行的就是__init__方法,該方法的入參,其實就是BasePage類的入參。 19 # __init__方法不能有返回值,只能返回None 20 # self指實例本身,相較於類-Page而言。 21 22 """ 23 構造方法 24 :param driver: 封裝好的webdriver 25 :param base_url: 基本url:"http://www.pzcnet.com/" 26 :param PROJECT_DIR: 項目所在目錄 27 """ 28 def __init__(self): 29 """ 30 # 定義driver和base_url 31 """ 32 driver = webdriver.Firefox() 33 try: 34 self.driver = driver 35 except Exception: 36 raise NameError("Firefox Not Found!") 37 38 self.base_url = "http://www.pzcnet.com/" 39 40 def captureScreenshot(self, filename): 41 # 截取屏幕截圖並保存到指定路徑 42 try: 43 self.driver.get_screenshot_as_file(filename) 44 except BaseException as e: 45 print('保存屏幕截圖失敗,失敗信息:' + e) 46 47 48 def operationCheck(self, Name, isSucceed): 49 ''' 50 判斷運行是否成功: 51 1、若成功,輸出運行成功; 52 2、若不成功,則對失敗頁面進行屏幕截圖,並保存: 53 (1)保存路徑為'screenshot'--day_now--Name_time_now.png; 54 (2)保存前先判斷'screenshot'文件夾下是否存在day_now文件夾,若不存在,則先新建'day_now'文件夾 55 (3)保存的文件名為 “所檢查的操作名Name”_“所檢查的時間time_now”.png 56 ''' 57 # 判斷運行是否成功 58 if isSucceed: 59 print(Name + u":運行成功!") 60 61 else: 62 day_now = time.strftime("%Y%m%d", time.localtime(time.time())) 63 time_now = time.strftime("%H%M%S", time.localtime(time.time())) 64 path = os.path.join(project_dir, 'screenshot', day_now) 65 if not os.path.exists(path): 66 os.mkdir(path) 67 png_path = os.path.join(path, Name + "_" + time_now + '.png') 68 self.captureScreenshot(png_path) 69 print(Name + u":運行失敗!請查看截圖快照:" + png_path) 70 self.driver.quit() 71 72 def openPage(self, url): 73 """ 74 打開頁面,通過拼接URL的方式 75 :param url: 76 :return: 77 """
-
Sub Pages(s)子類
- 具體的頁面的類,定義了某個具體的頁面的功能。
例如:在盤中餐網站中實現消費者的登錄,主要功能未:消費者輸入用戶名-密碼-驗證碼-點擊“登錄”-跳轉到訂餐首頁
1. 引入BasePage中的所有基本方法
1 from pages import BasePage
2. 根據該頁面的具體功能設計方法,例如消費者登錄頁面(CustomerLogin.py)
1 class customer_login(BasePage.base_page): 2 def __init__(self): 3 BasePage.base_page.__init__(self) 4 self.customer_login_page_url = 'customer/toLogin' 5 self.order_main_page_url = 'customer/toIndex' 6 7 def customer_account_login(self, user_name, user_password, captcha): 8 self.openPage(self.customer_login_page_url) 9 self.sendkeys("id,login-username", user_name) 10 self.sendkeys("id,login-password", user_password) 11 self.sendkeys("id,checkCode",captcha) 12 self.click("name,commit") 13 14 def get_orderMainPage(self): 15 return self.base_url + self.order_main_page_url
-
Tests 類
- 這部分描述的是具體的測試用例。
- 一個頁面對應一個測試用例
- 測試用例都以test_開頭,因為在使用unittest.main()時,默認會執行所有以test開頭的測試用例
例如:對於消費者登錄頁面(CustomerLogin.py)對應一個測試用例(test_CustomerLogin.py)
1. 繼承對應頁面(CustomerLogin.py)中所有的方法
1 from pages import CustomerLogin
2. 聲明一個操作員operator,用操作員來調用BasePage中的方法
1 # coding = utf-8 2 3 import unittest 4 from pages import CustomerLogin 5 6 class CustomerLoginTest(unittest.TestCase): 7 8 #在用例類中,只需要聲明一個操作員(operator),用操作員來調用basepage類中的方法便可 9 def setUp(self): 10 self.operator = CustomerLogin.customer_login() 11 12 def test_customer_account_login(self): 13 u"""消費者登錄""" 14 operator = self.operator 15 user_name = "xxxx" 16 user_password = "xxxx" 17 captcha = "xxxx" 18 operator.customer_account_login(user_name, user_password, captcha) 19 operator.get_orderMainPage() 20 21 def tearDown(self): 22 pass 23 24 if __name__ == "__main__": 25 unittest.main()
-
run_test 執行所用用例
1. 引入全局變量或方法:
1 from public import GlobalValue 2 project_dir = GlobalValue.global_value.PROJECT_DIR 3 testcase_dir = project_dir + "\\testcase"
2. 將測試目錄testcase添加到系統的path
1 import sys 2 sys.path.append(testcase_dir)
3. 定義Test Suite
-
- 用discover()方法找到testcase下面所有的測試用例
- 將discover篩選出的測試用例,循環添加到測試套件中
1 import unittest 2 import HTMLTestRunner 3 import time 4 5 def create_suite(): 6 7 test_unit = unittest.TestSuite() 8 9 # 1. discover()方法定義,找到testcase目錄下面所有的用例 10 # TestLoader:測試用例加載器,包含多個加載測試用例的方法,返回一個測試套件 11 # 其中的discover方法:discover(start_dir,pattern='test*.py',top_level_dir=None 12 # 到指定目錄下所有測試模塊,並可遞歸查到子目錄下的測試模塊,只有匹配到文件名才能被加載 13 discover = unittest.defaultTestLoader.discover( 14 testcase_dir, 15 pattern='test_*.py', 16 # top_level_dir=None 17 ) 18 19 # 2. 將discover篩選出的用例,循環添加到測試套件中 20 for test_suit in discover: 21 for test_case in test_suit: 22 test_unit.addTest(test_case) 23 print(test_unit) 24 return test_unit
4. 生成HTML測試報告,(並通過郵件發送到指定郵箱——暫未實現)
1 # 創建測試報告文件 2 def run_and_generate_report(test_object): 3 now = time.strftime('%Y-%m-%d_%H_M_S', time.localtime(time.time())) 4 filename = project_dir + "\\report\\" + now + 'report.html' 5 fp = open(filename,'wb') 6 7 runner = HTMLTestRunner.HTMLTestRunner( 8 stream=fp, 9 title='測試報告', 10 description='用例執行情況:' 11 )
5. 定義測試的主入口
-
- 定義測試的主要入口類,代碼的入口
1 all_testSuit_names = create_suite() 2 run_and_generate_report(all_testSuit_names)