3分鐘解決web自動化基於selenium框架的搭建

1 、什麼是selenium
Selenium 是一個基於瀏覽器的自動化工具,它提供了一種跨平臺、跨瀏覽器的端到端的web自動化解決方案。Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid:

Selenium IDE:Firefox的一個擴展,它能夠進行錄製回放,並能夠把錄製的操做以多種語言(例如java,python等)的形式導出成測試用例。html

Selenium WebDriver:提供Web自動化所需的API,主要用做瀏覽器控制、頁面元素選擇和調試。不一樣的瀏覽器須要不一樣的WebDriver。java

Selenium Grid:提供了在不一樣機器的不一樣瀏覽器上運行selenium測試的能力python

2 、自動化測試框架
一個典型的自動化測試框架通常包括用例管理模塊、自動化執行控制器、報表生成模塊和日誌模塊等,這些模塊之間不是相互孤立的,而是相輔相成的。

下面來介紹下每一個模塊的邏輯單元:mysql

用例管理模塊web

用例管理模塊包括用例的添加、修改、刪除等操做單元,這些單元也會涉及到用例書寫的模式,測試數據的管理、可複用庫等面試

自動化執行控制器sql

控制器是自動化用例執行的組織模塊,主要負責以什麼方式去執行用例。比較典型的控制器有用戶圖形界面(GUI)和「commandline+文件」兩種。chrome

報表生成模塊shell

報表生成模塊主要負責執行完用例之後生成報表,報表通常以HTML格式居多,信息主要包括用例的執行狀況及相應的總結報告。另外還能夠添加發送郵件功能。編程

日誌模塊

日誌模塊主要用來記錄用例的執行狀況,以便於更高效的調查用例失敗信息及追蹤用例執行狀況。

三、 自動化框架的設計與實現
3.1 需求分析

測試對象是一個典型的後臺系統的Web展示平臺,基於此平臺設計的自動化框架要包含測試用例管理、測試執行控制、測試報表及測試日誌的生成,總體測試框架要輕量易用。

3.2 概要設計

概要設計包括了四個大的模塊:公共庫模塊(可複用函數、日誌管理、報表管理以及發送郵件管理)、用例倉庫(具體用例的管理)、頁面管理(單獨對Web頁面進行抽象,封裝頁面元素和操做方法)以及執行模塊。

概要設計類圖:

3.3 詳細設計與實現

3.3.1 頁面管理

測試Web對象是一個典型的單頁面應用,所以採用頁面模式(page pattern)來進行組織:

頁面模式是頁面與測試用例之間的橋樑,它將每一個頁面抽象成一個單獨的頁面類,爲測試用例提供頁面元素的定位和操做。

頁面模式的類圖以下:

BasePage做爲基類只包含一個driver成員變量,它用來標記Selenium中的WebDriver,以便在BasePage的派生類中定位頁面元素。LoginPage和PageN等做爲派生類,能夠提供相應頁面元素的定位和操做方法。好比測試對象的登陸頁面:

從頁面能夠看出,須要操做的頁面元素分別爲:Username,Password,remember my username checkbox和Sign in按鈕,它們對應的操做爲輸入用戶名和密碼,點選checkbox和點擊Sign In按鈕,具體代碼級別的實現以下:

頁面基類BasePage.py:

class BasePage(object): 
description of class

#webdriver instance 
def __init__(self, driver): 
self.driver = driver

LoginPage頁面繼承自BasePage,並進行Login Page的元素定位及操做實現。代碼中定位了username和password,而且添加了設置用戶名和密碼的操做。

我向你們推薦一個學習資料領取的qq羣:175317069

from BasePage import BasePage 
from selenium.webdriver.common.by import By 
from selenium.webdriver.common.keys import Keys 
 
class LoginPage(BasePage): 
"""description of class""" 
#page element identifier 
usename = (By.ID,'username') 
password = (By.ID, 'password') 
dialogTitle = (By.XPATH,"//h3[@class=\"modal-title ng-binding\"]") 
cancelButton = (By.XPATH,'//button[@class=\"btn btn-warning ng-binding\"][@ng-click=\"cancel()\"]') 
okButton = (By.XPATH,'//button[@class=\"btn btn-primary ng-binding\"][@ng-click=\"ok()\"]') 
 
#Get username textbox and input username 
def set_username(self,username): 
name = self.driver.find_element(*LoginPage.usename) 
name.send_keys(username) 
 
#Get password textbox and input password, then hit return 
def set_password(self, password): 
pwd = self.driver.find_element(*LoginPage.password) 
pwd.send_keys(password + Keys.RETURN) 
 
#Get pop up dialog title 
def get_DiaglogTitle(self): 
digTitle = self.driver.find_element(*LoginPage.dialogTitle) 
return digTitle.text 
 
#Get "cancel" button and then click 
def click_cancel(self): 
cancelbtn = self.driver.find_element(*LoginPage.cancelButton) 
cancelbtn.click() 
 
#click Sign in 
def click_SignIn(self): 
okbtn = self.driver.find_element(*LoginPage.okButton) 
okbtn.click()

採用頁面模式來管理頁面和測試用例有不少好處,主要體如今:

簡單而且清晰

每一個頁面都有單獨的類來封裝頁面元素和操做,讓頁面操做更加具體化,而不是相對獨立的。

好比未使用頁面模式,測試用例的輸入用戶名和密碼的代碼:

#enter username and password 
driver.find_element_by_id("username").clear() 
driver.find_element_by_id("username").send_keys
(("sbxadmin") 
driver.find_element_by_id("password").clear() 
driver.find_element_by_id("password").send_keys
("password"+Keys.RETURN)

使用頁面模式以後,輸入用戶名和密碼的代碼:

#Step2: Open Login page 
login_page = BasePage.LoginPage(self.driver) 
#Step3: Enter username 
login_page.set_username("username") 
#Step4: Enter password 
login_page.set_password("password")
 

經過對比咱們不難發現,未使用頁面模式的代碼組織比較混亂,步驟多,可讀性很是差,不難想象,一個通篇都是find_element_by_id或者send_Keys的測試用例到底有多糟糕!而使用了頁面模式以後,在哪一個頁面作什麼操做都很是清晰,很是接近測試用例的步驟,易讀性很是好。

可複用性好

因爲頁面操做都被封裝在了頁面類中,因此頁面方法和容易調用,可複用性很是好。而未使用頁面模式的用例只能每次都實現一遍。

可維護性好

因爲測試目標頁面的多變性,頁面元素的定位常常須要改變,利用了頁面模式後,只須要修改一遍其頁面類中的定位就能夠對所用用到該元素的測試用例生效;而在未使用該模式的狀況下,必須修改每個用到該元素的測試用例,很是容易遺漏,工做量也很是大。

綜合以上頁面模式的各類優勢,咱們在之後的web自動化中能夠多使用該模式來組織頁面。

3.3.2 公共庫模塊

公共庫模塊是爲建立測試用例服務的,它主要包括常量、公共函數、日誌管理、報表管理以及發送郵件管理等。

公共庫模塊涉及到的功能通常多而雜,在設計的時候只要遵循高內聚低耦合就能夠了。好比常量、變量和一些公共函數能夠放在同一個文件中Common.py

from datetime import datetime 
 
def driverPath(): 
return r'C:\Users\xua\Downloads\chromedriver_win32
\chromedriver.exe' 
def baseUrl(): 
return "https://xxx.xxx.xxx.xxx:9000" 
#change time to str 
def getCurrentTime(): 
format = "%a %b %d %H:%M:%S %Y" 
return datetime.now().strftime(format) 
# Get time diff 
def timeDiff(starttime,endtime): 
format = "%a %b %d %H:%M:%S %Y" 
return datetime.strptime(endtime,format) - datetime.strptime(starttime,format)

測試用例信息類用來標識測試用例,而且包括執行用例執行結果信息,主要包括如下字段:

class TestCaseInfo(object): 
"""description of class""" 
def __init__(self, id="",name="",owner="",result="Failed",starttime="",
endtime="",secondsDuration="",errorinfo=""): 
self.id = id 
self.name = name 
self.owner = owner 
self.result = result 
self.starttime = starttime 
self.endtime = endtime 
self.secondsDuration = secondsDuration 
self.errorinfo = errorinfo

測試用例信息須要在每一個測試用例中實例化,以便對測試用例進行標記,並最終體如今測試報告中。

日誌主要用來記錄測試用例執行步驟及產生的錯誤信息,不一樣的信息有不一樣的日誌級別,好比Information,Warning,Critical和Debug。因爲每一個測試用例產生的日誌條目比較少,因此在測試框架中只利用了最高級別的日誌打印,即Debug級別,該級別也會將其餘全部的日誌級別的信息一樣打印出來。在具體的實現中引用了Python標準庫中的logging類庫,以便更方便的控制日誌輸出:

import logging 
import ResultFolder 
 
logger = logging.getLogger() 
logger.setLevel(logging.DEBUG) 
 
 
def CreateLoggerFile(filename): 
try: 
fulllogname = ResultFolder.GetRunDirectory()+"\\"+filename+".log" 
fh = logging.FileHandler(fulllogname) 
fh.setLevel(logging.DEBUG) 
formatter = logging.Formatter('%(asctime)s [line:%(lineno)d] %(message)s') 
fh.setFormatter(formatter) 
logger.addHandler(fh) 
except Exception as err: 
logger.debug("Error when creating log file, error message: {}".format(str(err))) 
 
 
def Log(message): 
logger.debug(message)

報表管理及發送郵件模塊實現了報表(html格式)的生成及自動發送郵件的功能。報表和郵件依附於當前測試的執行,每次執行都會獨立的觸發報表生成和郵件發送。該模塊主要運用了Python中的lxml、smtplib和email庫。

3.3.3 用例倉庫

用例倉庫主要用來組織自動化測試用例。每條測試用例都被抽象成一個獨立的類,而且均繼承自unittest.TestCase類。 Python中的unittest庫提供了豐富的測試框架支持,包括測試用例的setUp和tearDown方法,在實現用例的過程當中能夠重寫。依託頁面管理和公共庫模塊實現的頁面方法和公共函數,每個測試用例腳本的書寫都會很是清晰簡潔,一個簡單的Floor Manager Lite的登陸用例以下:

class Test_TC_Login(unittest.TestCase): 
def setUp(self): 
self.driver = webdriver.Chrome(cc.driverPath()) 
self.base_url = cc.baseUrl() 
self.testCaseInfo = TestCaseInfo(id=1,name="Test case name",owner='xua') 
self.testResult = TestReport() 
LogUtility.CreateLoggerFile("Test_TC_Login") 
def test_A(self): 
try: 
self.testCaseInfo.starttime = cc.getCurrentTime() 
#Step1: open base site 
LogUtility.Log("Open Base site"+self.base_url) 
self.driver.get(self.base_url) 
 
#Step2: Open Login page 
login_page = LoginPage(self.driver) 
 
#Step3: Enter username & password 
LogUtility.Log("Login web using username") 
login_page.set_username("username") 
login_page.set_password("password") 
 
time.sleep(2) 
#Checkpoint1: Check popup dialog title 
LogUtility.Log("Check whether sign in dialog exists or not") 
self.assertEqual(login_page.get_DiaglogTitle(),"Sign in") 
 
#time.sleep(3) 
#Step4: Cancel dialog 
login_page.click_cancel() 
self.testCaseInfo.result = "Pass" 
 
except Exception as err: 
self.testCaseInfo.errorinfo = str(err) 
LogUtility.Log(("Got error: "+str(err))) 
finally: 
self.testCaseInfo.endtime = cc.getCurrentTime() 
self.testCaseInfo.secondsDuration = cc.timeDiff(self.testCaseInfo.starttime,self.
testCaseInfo.endtime) 
def tearDown(self): 
self.driver.close() 
self.testResult.WriteHTML(self.testCaseInfo) 
 
if __name__ == '__main__': 
unittest.main()

從這個測試用例中,咱們能夠看到

Setup中定義了執行測試用例前的一些實例化工做

tearDown對執行完測試作了清理和寫日誌文件工做

測試步驟、測試數據和測試檢查點很是清晰,易修改(好比用戶名密碼)

日誌級別僅有Debug,因此寫日誌僅需用同一Log方法

3.3.4 用例執行模塊(控制器)

執行模塊主要用來控制測試用例腳本的批量執行,造成一個測試集。用例的執行引用了Python標準庫中的subprocess來執行nosetests的shell命令,從而執行給定測試用例集中的用例。測試用例集是一個簡單的純文本文件,實現過程當中利用了.txt文件testcases.txt:

Test_Login_pass.py 
Test_Login_Fail.py 
#Test_MainPage_CheckSecurityTableInfo.py 
Test_MainPage_EditSecurityInfo.py

 

用例前沒有「#「標記的測試用例腳本會被執行,而有」#「標記的則會被忽略,這樣能夠很方便的控制測試集的執行,固然也能夠建立不一樣的文件來執行不一樣的測試集。

當真正開始學習的時候頻繁踩坑,最終浪費大量時間,因此有一套實用的視頻資料用來跟着學習是很是有必要的。我向你們推薦一個學習資料領取的qq羣。

這套視頻資料詳細講解了(自動化編程,mysql調優,自動化框架使用)。

那麼,這套視頻咱們應該怎麼獲取呢?

對以上測試資料,測試技術 感興趣的朋友,歡迎加QQ羣:175317069,一塊兒學習,相互討論。

羣內已經有小夥伴將知識體系整理好(筆記,學習視頻,面試題),歡迎加羣免費取。

具體的調用代碼以下:

def LoadAndRunTestCases(self): 
try: 
f = open(self.testcaselistfile) 
testfiles = [test for test in f.readlines() if not test.startswith("#")] 
f.close() 
for item in testfiles: 
subprocess.call("nosetests "+str(item).replace("\\n",""),shell = True) 
except Exception as err: 
LogUtility.logger.debug("Failed running test cases, error message: {}".format(str(err))) 
finally: 
EmailUtils.send_report()

3.4 執行結果

測試用例執行完畢後主要有兩種輸出:日誌和測試報告。測試報告會html附件的形式經過郵件發出,例如:

四、 須要改進的模塊
對於現有實現的測試框架,已經能夠知足web對象的自動化需求,但仍是有些能夠改進提升的地方,好比:

針對部分測試用例是否能夠嘗試數據驅動

添加屏幕截圖功能

封裝selenium中By庫中的函數,以便更高效的定位頁面元素等

結合業界優秀的自動化框架和實踐持續改進

5 、總結 基於selenium實現的web自動化框架不只輕量級並且靈活,能夠快速的開發自動化測試用例。結合本篇中的框架設計以及一些好的實踐,但願對你們之後的web自動化框架的設計和實現有所幫助。

相關文章
相關標籤/搜索