1 什么是selenium
Selenium 是一個(gè)基于瀏覽器的自動(dòng)化工具,它提供了一種跨平臺(tái)、跨瀏覽器的端到端的web自動(dòng)化解決方案。Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid:
- Selenium IDE:Firefox的一個(gè)擴(kuò)展,它可以進(jìn)行錄制回放,并可以把錄制的操作以多種語(yǔ)言(例如java,python等)的形式導(dǎo)出成測(cè)試用例。
- Selenium WebDriver:提供Web自動(dòng)化所需的API,主要用作瀏覽器控制、頁(yè)面元素選擇和調(diào)試。不同的瀏覽器需要不同的WebDriver。
-
Selenium Grid:提供了在不同機(jī)器的不同瀏覽器上運(yùn)行selenium測(cè)試的能力
本文中主要使用python結(jié)合Selenium WebDriver庫(kù)進(jìn)行自動(dòng)化測(cè)試框架的搭建。
2 自動(dòng)化測(cè)試框架
一個(gè)典型的自動(dòng)化測(cè)試框架一般包括用例管理模塊、自動(dòng)化執(zhí)行控制器、報(bào)表生成模塊和日志模塊等,這些模塊之間不是相互孤立的,而是相輔相成的。
下面來(lái)介紹下每個(gè)模塊的邏輯單元:
- 用例管理模塊
用例管理模塊包括用例的添加、修改、刪除等操作單元,這些單元也會(huì)涉及到用例書(shū)寫(xiě)的模式,測(cè)試數(shù)據(jù)的管理、可復(fù)用庫(kù)等
- 自動(dòng)化執(zhí)行控制器
控制器是自動(dòng)化用例執(zhí)行的組織模塊,主要負(fù)責(zé)以什么方式去執(zhí)行用例。比較典型的控制器有用戶(hù)圖形界面(GUI)和“commandline+文件”兩種。
- 報(bào)表生成模塊
報(bào)表生成模塊主要負(fù)責(zé)執(zhí)行完用例以后生成報(bào)表,報(bào)表一般以HTML格式居多,信息主要包括用例的執(zhí)行情況及相應(yīng)的總結(jié)報(bào)告。另外還可以添加發(fā)送郵件功能。
- 日志模塊
日志模塊主要用來(lái)記錄用例的執(zhí)行情況,以便于更高效的調(diào)查用例失敗信息及追蹤用例執(zhí)行情況。
3 自動(dòng)化框架的設(shè)計(jì)與實(shí)現(xiàn)
3.1 需求分析
測(cè)試對(duì)象是一個(gè)典型的后臺(tái)系統(tǒng)的Web展現(xiàn)平臺(tái),基于此平臺(tái)設(shè)計(jì)的自動(dòng)化框架要包含測(cè)試用例管理、測(cè)試執(zhí)行控制、測(cè)試報(bào)表及測(cè)試日志的生成,整體測(cè)試框架要輕量易用。
3.2 概要設(shè)計(jì)
概要設(shè)計(jì)包括了四個(gè)大的模塊:公共庫(kù)模塊(可復(fù)用函數(shù)、日志管理、報(bào)表管理以及發(fā)送郵件管理)、用例倉(cāng)庫(kù)(具體用例的管理)、頁(yè)面管理(單獨(dú)對(duì)Web頁(yè)面進(jìn)行抽象,封裝頁(yè)面元素和操作方法)以及執(zhí)行模塊。
概要設(shè)計(jì)類(lèi)圖:
3.3 詳細(xì)設(shè)計(jì)與實(shí)現(xiàn)
3.3.1 頁(yè)面管理
測(cè)試Web對(duì)象是一個(gè)典型的單頁(yè)面應(yīng)用,因此采用頁(yè)面模式(page pattern)來(lái)進(jìn)行組織:
頁(yè)面模式是頁(yè)面與測(cè)試用例之間的橋梁,它將每個(gè)頁(yè)面抽象成一個(gè)單獨(dú)的頁(yè)面類(lèi),為測(cè)試用例提供頁(yè)面元素的定位和操作。
頁(yè)面模式的類(lèi)圖如下:
BasePage作為基類(lèi)只包含一個(gè)driver成員變量,它用來(lái)標(biāo)記Selenium中的WebDriver,以便在BasePage的派生類(lèi)中定位頁(yè)面元素。LoginPage和PageN等作為派生類(lèi),可以提供相應(yīng)頁(yè)面元素的定位和操作方法。比如測(cè)試對(duì)象的登錄頁(yè)面:
從頁(yè)面可以看出,需要操作的頁(yè)面元素分別為:Username,Password,remember my username checkbox和Sign in按鈕,它們對(duì)應(yīng)的操作為輸入用戶(hù)名和密碼,點(diǎn)選checkbox和點(diǎn)擊Sign In按鈕,具體代碼級(jí)別的實(shí)現(xiàn)如下:
頁(yè)面基類(lèi)BasePage.py:
class BasePage(object): """description of class""" #webdriver instance def __init__(self, driver): self.driver = driver
LoginPage頁(yè)面繼承自BasePage,并進(jìn)行Login Page的元素定位及操作實(shí)現(xiàn)。代碼中定位了username和password,并且添加了設(shè)置用戶(hù)名和密碼的操作。
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,"http://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()
采用頁(yè)面模式來(lái)管理頁(yè)面和測(cè)試用例有很多好處,主要體現(xiàn)在:
- 簡(jiǎn)單并且清晰
每個(gè)頁(yè)面都有單獨(dú)的類(lèi)來(lái)封裝頁(yè)面元素和操作,讓頁(yè)面操作更加具體化,而不是相對(duì)獨(dú)立的。
比如未使用頁(yè)面模式,測(cè)試用例的輸入用戶(hù)名和密碼的代碼:
#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)
使用頁(yè)面模式之后,輸入用戶(hù)名和密碼的代碼:
#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")
通過(guò)對(duì)比我們不難發(fā)現(xiàn),未使用頁(yè)面模式的代碼組織比較混亂,步驟多,可讀性非常差,不難想象,一個(gè)通篇都是find_element_by_id或者send_Keys的測(cè)試用例到底有多糟糕!而使用了頁(yè)面模式之后,在哪個(gè)頁(yè)面做什么操作都非常清晰,非常接近測(cè)試用例的步驟,易讀性非常好。
- 可復(fù)用性好
由于頁(yè)面操作都被封裝在了頁(yè)面類(lèi)中,所以頁(yè)面方法和容易調(diào)用,可復(fù)用性非常好。而未使用頁(yè)面模式的用例只能每次都實(shí)現(xiàn)一遍。
- 可維護(hù)性好
由于測(cè)試目標(biāo)頁(yè)面的多變性,頁(yè)面元素的定位經(jīng)常需要改變,利用了頁(yè)面模式后,只需要修改一遍其頁(yè)面類(lèi)中的定位就可以對(duì)所用用到該元素的測(cè)試用例生效;而在未使用該模式的情況下,必須修改每一個(gè)用到該元素的測(cè)試用例,非常容易遺漏,工作量也非常大。
綜合以上頁(yè)面模式的各種優(yōu)點(diǎn),我們?cè)谝院蟮膚eb自動(dòng)化中可以多使用該模式來(lái)組織頁(yè)面。
3.3.2 公共庫(kù)模塊
公共庫(kù)模塊是為創(chuàng)建測(cè)試用例服務(wù)的,它主要包括常量、公共函數(shù)、日志管理、報(bào)表管理以及發(fā)送郵件管理等。
公共庫(kù)模塊涉及到的功能一般多而雜,在設(shè)計(jì)的時(shí)候只要遵循高內(nèi)聚低耦合就可以了。比如常量、變量和一些公共函數(shù)可以放在同一個(gè)文件中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)
測(cè)試用例信息類(lèi)用來(lái)標(biāo)識(shí)測(cè)試用例,并且包括執(zhí)行用例執(zhí)行結(jié)果信息,主要包括以下字段:
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
測(cè)試用例信息需要在每個(gè)測(cè)試用例中實(shí)例化,以便對(duì)測(cè)試用例進(jìn)行標(biāo)記,并最終體現(xiàn)在測(cè)試報(bào)告中。
日志主要用來(lái)記錄測(cè)試用例執(zhí)行步驟及產(chǎn)生的錯(cuò)誤信息,不同的信息有不同的日志級(jí)別,比如Information,Warning,Critical和Debug。由于每個(gè)測(cè)試用例產(chǎn)生的日志條目比較少,所以在測(cè)試框架中只利用了最高級(jí)別的日志打印,即Debug級(jí)別,該級(jí)別也會(huì)將其他所有的日志級(jí)別的信息同樣打印出來(lái)。在具體的實(shí)現(xiàn)中引用了Python標(biāo)準(zhǔn)庫(kù)中的logging類(lèi)庫(kù),以便更方便的控制日志輸出:
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)
報(bào)表管理及發(fā)送郵件模塊實(shí)現(xiàn)了報(bào)表(html格式)的生成及自動(dòng)發(fā)送郵件的功能。報(bào)表和郵件依附于當(dāng)前測(cè)試的執(zhí)行,每次執(zhí)行都會(huì)獨(dú)立的觸發(fā)報(bào)表生成和郵件發(fā)送。該模塊主要運(yùn)用了Python中的lxml、smtplib和email庫(kù)。
3.3.3 用例倉(cāng)庫(kù)
用例倉(cāng)庫(kù)主要用來(lái)組織自動(dòng)化測(cè)試用例。每條測(cè)試用例都被抽象成一個(gè)獨(dú)立的類(lèi),并且均繼承自u(píng)nittest.TestCase類(lèi)。 Python中的unittest庫(kù)提供了豐富的測(cè)試框架支持,包括測(cè)試用例的setUp和tearDown方法,在實(shí)現(xiàn)用例的過(guò)程中可以重寫(xiě)。依托頁(yè)面管理和公共庫(kù)模塊實(shí)現(xiàn)的頁(yè)面方法和公共函數(shù),每一個(gè)測(cè)試用例腳本的書(shū)寫(xiě)都會(huì)非常清晰簡(jiǎn)潔,一個(gè)簡(jiǎn)單的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()
從這個(gè)測(cè)試用例中,我們可以看到
1.Setup中定義了執(zhí)行測(cè)試用例前的一些實(shí)例化工作
2.tearDown對(duì)執(zhí)行完測(cè)試做了清理和寫(xiě)日志文件工作
3.測(cè)試步驟、測(cè)試數(shù)據(jù)和測(cè)試檢查點(diǎn)非常清晰,易修改(比如用戶(hù)名密碼)
4.日志級(jí)別僅有Debug,所以寫(xiě)日志僅需用同一Log方法
3.3.4 用例執(zhí)行模塊(控制器)
執(zhí)行模塊主要用來(lái)控制測(cè)試用例腳本的批量執(zhí)行,形成一個(gè)測(cè)試集。用例的執(zhí)行引用了Python標(biāo)準(zhǔn)庫(kù)中的subprocess來(lái)執(zhí)行nosetests的shell命令,從而執(zhí)行給定測(cè)試用例集中的用例。測(cè)試用例集是一個(gè)簡(jiǎn)單的純文本文件,實(shí)現(xiàn)過(guò)程中利用了.txt文件testcases.txt:
Test_Login_pass.py Test_Login_Fail.py #Test_MainPage_CheckSecurityTableInfo.py Test_MainPage_EditSecurityInfo.py
用例前沒(méi)有“#“標(biāo)記的測(cè)試用例腳本會(huì)被執(zhí)行,而有”#“標(biāo)記的則會(huì)被忽略,這樣可以很方便的控制測(cè)試集的執(zhí)行,當(dāng)然也可以創(chuàng)建不同的文件來(lái)執(zhí)行不同的測(cè)試集。
具體的調(diào)用代碼如下:
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 執(zhí)行結(jié)果
測(cè)試用例執(zhí)行完畢后主要有兩種輸出:日志和測(cè)試報(bào)告。測(cè)試報(bào)告會(huì)html附件的形式通過(guò)郵件發(fā)出,例如:
4 需要改進(jìn)的模塊
對(duì)于現(xiàn)有實(shí)現(xiàn)的測(cè)試框架,已經(jīng)可以滿(mǎn)足web對(duì)象的自動(dòng)化需求,但還是有些可以改進(jìn)提高的地方,比如:
針對(duì)部分測(cè)試用例是否可以嘗試數(shù)據(jù)驅(qū)動(dòng)添加屏幕截圖功能封裝selenium中By庫(kù)中的函數(shù),以便更高效的定位頁(yè)面元素等結(jié)合業(yè)界優(yōu)秀的自動(dòng)化框架和實(shí)踐持續(xù)改進(jìn)
5 總結(jié)
基于selenium實(shí)現(xiàn)的web自動(dòng)化框架不僅輕量級(jí)而且靈活,可以快速的開(kāi)發(fā)自動(dòng)化測(cè)試用例。結(jié)合本篇中的框架設(shè)計(jì)以及一些好的實(shí)踐,希望對(duì)大家以后的web自動(dòng)化框架的設(shè)計(jì)和實(shí)現(xiàn)有所幫助。
源代碼:https://github.com/AlvinXuCH/WebAutomaiton
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
