行爲驅動(2)
五、lettuce框架的步驟數據表格
實例:
(1)在PyCharm工具中建立以下所示的目錄結構及文件
..\lettuce\StepDataTables\features\student.features
..\lettuce\StepDataTables\features\steps.py
(2)student.feature文件具體內容以下 所示:ios
Feature: bill students alphabetically In order to bill students properly As a financial specialist I want to bill those which name starts with some letter Scenario: Bill students which name starts with "G" Given I have the following students in my database: | name | monthly_due | billed | | Anton | $ 500 | no | | Jack | $ 400 | no | | Gabriel | $ 300 | no | | Gloria | $ 442.65 | no | | Ken | $ 907.86 | no | | Leonard | $ 742.84 | no | When I bill names starting with "G" Then I see those billed students: | name | monthly_due | billed | | Gabriel | $ 300 | no | | Gloria | $ 442.65 | no | And those that weren't: | name | monthly_due | billed | | Anton | $ 500 | no | | Jack | $ 400 | no | | Ken | $ 907.86 | no | | Leonard | $ 742.84 | no |
Given、Then及And步驟下都存在步驟數據表格,數據間以「|」進行分隔,數據表的第一行表示數據表的列名,不做爲數據存在。
(3)steps.py文件,用於編寫獲取student.feature文件中的數據,並提供後續操做,具體內容以下:web
#encoding=utf-8 from lettuce import * @step('I have the following students in my database:') def students_in_database(step): if step.hashes: # 若是存在步驟表格數據,則繼續後續步驟 print type(step.hashes) assert step.hashes == [ { 'name': 'Anton', 'monthly_due': '$ 500', 'billed': 'no' }, { 'name': 'Jack', 'monthly_due': '$ 400', 'billed': 'no' }, { 'name': 'Gabriel', 'monthly_due': '$ 300', 'billed': 'no' }, { 'name': 'Gloria', 'monthly_due': '$ 442.65', 'billed': 'no' }, { 'name': 'Ken', 'monthly_due': '$ 907.86', 'billed': 'no' }, { 'name': 'Leonard', 'monthly_due': '$ 742.84', 'billed': 'no' }, ] @step('I bill names starting with "(.*)"') def match_starting(step, startAlpha): # 將經過正則表達式匹配步驟中最後一個字母, # 並存於全局變量startAlpha中 world.startAlpha = startAlpha print step.hashes @step('I see those billed students:') def get_starting_with_G_student(step): # 遍歷步驟數據表中的數據 for i in step.hashes: # 斷言學生的名字是否以world.startAlpha變量存取的的字母開頭 assert i["name"].startswith(world.startAlpha) @step("those that weren't:") def result(step): for j in step.hashes: # 斷言學生名字不以world.startAlpha變量存取的的字母開頭 assert world.startAlpha not in j["name"][0]
六、 使用WebDriver進行英文語言的行爲數據驅動測試
測試邏輯:
(1)訪問http://www.sogou.com
(2)依次搜索幾個球星的英文名字中的一部分
(3)在搜索結果頁面斷言搜索的球星的全名正則表達式
實例:
(1)在PyCharm工具中建立以下所示的目錄結構及文件:
..lettuce\BddDataDrivenByEnglish\features\sogou.feature
..lettuce\BddDataDrivenByEnglish\features\sogou.py
..lettuce\BddDataDrivenByEnglish\features\terrain.py
(2)sogou.feature文件用於存放數據驅動所須要的數據,具體內容以下:shell
:Search in Sogou website In order to Search in Sogou website As a visitor We'll search the NBA best player Scenario:Search NBA player Given I have the english name "<search_name>" When I search it in Sogou website Then i see the entire name "<search_result>" Examples: |search_name |search_result | |Jordan |Michael | |Curry |Stephen | |Kobe |Bryant |
Examples下面是一個場景數據表,數據間以「|」進行分隔,數據表的第一行表示數據表的列名,與場景中的變量名對應,好比Given I have the english name "<search_name>"語句中的search_name對應數據表中的search_name列
(3)sogou.py文件編寫實施結合行爲驅動的數據驅動測試,具體內容以下:編程
#encoding=utf-8 from lettuce import * from selenium import webdriver import time @step('I have the english name "(.*)"') def have_the_searchWord(step,searchWord): world.searchWord = str(searchWord) print world.searchWord @step('I search it in Sogou website') def search_in_sogou_website(step,): world.driver = webdriver.Firefox(executable_path = "D:\\geckodriver") world.driver.get("http://www.sogou.com") world.driver.find_element_by_id("query").send_keys(world.searchWord) world.driver.find_element_by_id("stb").click() time.sleep(3) @step('I see the entire name "(.*)"') def check_result_in_sogou(step,searchResult): assert searchResult in world.driver.page_source,"got word: %S" % searchResult world.driver.quit()
(4)terrain.py文件用於在測試過程當中和測試結束後打印日誌,具體內容以下:瀏覽器
#encoding=utf-8 from lettuce import * import logging #初始化日誌對象 logging.basicConfig( # 日誌級別 level=logging.INFO, #日誌格式 #時間、代碼所在文件名、代碼行號、日誌級別名字、日誌信息 format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', #打印日誌的時間 datefmt='%a, %Y-%m-%d %H:%M:%S', #日誌文件存放的目錄(目錄必須存在)及日誌文件名 filename = "D:\\PythonProject\\BDD\\lettuce\\BddDataDrivenByEnglish\\BddDataDrivenReport.log", #打開日誌的方式 filemode = "w" ) #在全部場景執行前執行 @before.all def say_hello(): logging.info("Lettuce will start to run tests right now...") print "Lettuce will start to run tests right now..." #在每一個Scenario開始執行前執行 @before.each_scenario def setup_some_scenario(scenario): #每一個Scenario開始前,打印場景的名字 print "Begin to execute scenario name: " + scenario.name #將開始執行的場景信息打印到日誌 logging.info("Begin to execute scenario name: " + scenario.name) #每一個step開始前執行 @before.each_step def setup_some_step(step): run = "running step % r,defind at % s" % ( step.sentence, #執行的步驟 step.defined_at.file #步驟定義在哪一個文件 ) #將每一個場景的每一步信息打印到日誌 logging.info(run) #每一個step執行後執行 @after.each_step def teardown_some_step(step): logging.info("End of the:'%s'" % step.sentence) #在每一個Scenario執行結束執行 @after.each_scenario def teardown_some_scenario(scenario): print "Finished,scenario name: " + scenario.name logging.info("Finished,scenario name: " + scenario.name) #在全部場景執行結束後執行 @after.all #默認獲取執行結果的對象做爲total參數 def say_goodbye(total): result = "Congratulations,%d of %d scenarios passed!" % ( total.scenarios_ran, #一共多少場景運行了 total.scenarios_passed #一共多少場景執行成功了 ) print result #將測試結果寫入日誌文件 logging.info(result) logging.info("Goodbye!") print "------------Goodbye!-------------"
七、使用WebDriver進行中文語言的行爲數據驅動測試
測試邏輯:
(1)訪問 http://www.baidu.com
(2)依次搜索幾本中文名字的書
(3)在搜索結果頁面斷言是否出現書的預期做者
實例:
(1)在PyCharm工具中建立以下所示的目錄結構及文件
..lettuce\BddDataDrivenByChinese\features\baidu.feature
..lettuce\BddDataDrivenByChinese\features\baidu.py
..lettuce\BddDataDrivenByChinese\features\terrain.py
..lettuce\BddDataDrivenByChinese\features\log.py
(2)baidu.feature文件內容以下:框架
#encoding=utf-8 # language: zh-CN 特性: 在百度網址搜索IT相關書籍 可以搜索到書的做者,好比吳曉華 場景: 在百度網站搜索IT相關書籍 若是將搜索詞設定爲書的名字"<書名>" 當打開百度網站 和在搜索輸入框中輸入搜索的關鍵詞,並點擊搜索按鈕後 那麼在搜索結果中能夠看到書的做者"<做者>" 例如: | 書名 | 做者 | | Selenium WebDriver實戰寶典 | 吳曉華 | | HTTP權威指南 | 協議 | | Python核心編程 | Python |
(3)baidu.py文件內容以下:函數
#encoding=utf-8 #language:zh-CN from lettuce import * from selenium import webdriver import time @step(u'將搜索詞設定爲書的名字"(.*)"') def have_the_searchWord(step,searchWord): world.searchWord = searchWord print world.searchWord @step(u'打開百度網站') def visit_baidu_website(step): world.driver = webdriver.Firefox(executable_path = "D:\\geckodriver") world.driver.get("http://www.baidu.com") @step(u'在搜索輸入框中輸入搜索的關鍵詞,並點擊搜索按鈕後') def search_in_sogou_website(step): world.driver.find_element_by_id("kw").send_keys(world.searchWord) world.driver.find_element_by_id("su").click() time.sleep(3) @step(u'在搜索結果中能夠看到書的做者"(.*)"') def check_result_in_sogou(step,searchResult): assert searchResult in world.driver.page_source , "not got words: %s" % searchResult world.driver.quit()
(4)terrain.py文件內容以下:工具
#encoding=utf-8 from lettuce import * from log import * #在全部場景執行前執行 @before.all def say_hello(): logging.info(u"開始執行行爲數據驅動測試...") #在每一個scenario開始前執行 @before.each_scenario def setup_some_scenario(scenario): #將開始執行的場景信息打印到日誌 logging.info(u'開始執行場景:"%s"' % scenario.name) #在每一個step開始前執行 @before.each_step def setup_some_step(step): world.stepName = step.sentence run = u'執行步驟" %s ",定義在"%s"文件中' % ( step.sentence, #執行的步驟 step.defined_at.file #步驟定義在哪一個文件 ) #將每一個場景的每一步信息打印到日誌 logging.info(run) #在每一個step執行後執行 @after.each_step def teardown_some_step(step): logging.info(u'步驟「%s」執行結束' % world.stepName) #在每一個scenario執行結束後執行 @after.each_scenario def teardown_some_scenario(scenario): logging.info(u'場景「%s」執行結束' % scenario.name) #在全部場景執行結束後執行 @after.all #默認獲取執行結果的對象做爲total參數 def say_goodbye(total): result = u"恭喜,%d個場景運行,%d個場景運行成功" % ( total.scenarios_ran, #一共多少場景運行了 total.scenarios_passed #一共多少場景運行成功 ) #將測試結束寫入日誌文件 logging.info(result) logging.info(u'本次行爲數據驅動執行結束')
(5)log.py文件用於編寫初始化日誌對象的程序,具體內容以下:測試
#encoding=utf-8 import logging #初始化日誌對象 logging.basicConfig( # 日誌級別 level=logging.INFO, #日誌格式 #時間、代碼所在文件名、代碼行號、日誌級別名字、日誌信息 format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', #打印日誌的時間 datefmt='%a, %Y-%m-%d %H:%M:%S', #日誌文件存放的目錄(目錄必須存在)及日誌文件名 filename = "D:\\PythonProject\\BDD\\lettuce\\BddDataDrivenByChinese\\BddDataDrivenReport.log", #打開日誌的方式 filemode = "w" )
Lettuce支持中文語言編寫的測試場景,是經過在場景文件以及測試場景的腳本文件的頂部添加# language:zh-CN,聲明使用的語言爲中文,同時,feature文件的編碼必須保存爲utf-8
八、批量執行行爲驅動用例集
lettuce支持一次執行多個用例,也就是放到features目錄下的多個.feature文件。
實例:
在PyCharm工具中建立以下所示的目錄結構及文件
..lettuce\MultipleFeatures\feature\Login_Chinese.feature
..lettuce\MultipleFeatures\feature\Login_Chinese.py
..lettuce\MultipleFeatures\feature\Login_English.feature
..lettuce\MultipleFeatures\feature\Login_English.py
..lettuce\MultipleFeatures\terrain.py
Login_Chinese.feature文件具體內容以下:
#encoding=utf-8 # language: zh-CN 特性: 登陸126郵箱和退出126郵箱登陸 場景: 成功登陸126郵箱 假如啓動一個瀏覽器 當用戶訪問https://mail.126.com網址 當用戶輸入用戶名「chenyl_2019」和密碼「sxzycyl0910」 那麼頁面會出現「未讀郵件」關鍵字 場景: 成功退出126郵箱 當用戶從頁面單擊退出連接 那麼頁面顯示「您已成功退出網易郵箱」關鍵內容
Login_Chinese.py文件內容以下:
#encoding=utf-8 #language:zh-CN from lettuce import * from selenium import webdriver from selenium.webdriver.common.keys import Keys import time @step(u'啓動一個瀏覽器') def open_browser(step): try: #建立Chrome瀏覽器的driver實例,並在於全局對象world中,供後續場景或步驟函數使用 world.driver = webdriver.Firefox(executable_path = "D:\\geckodriver") #瀏覽器窗口最大化 world.driver.maximize_window() time.sleep(3) except Exception,e: raise e @step(u'用戶訪問(.*)網址') def visit_url(step,url): print url world.driver.get(url) @step(u'用戶輸入用戶名"(.*)"和密碼"(.*)"') def user_enters_UserName_and_Password(step,username,password): print username,password # 點擊切換成帳號密碼登陸 world.driver.find_element_by_id("lbNormal").click() time.sleep(1) # 找到並切換進iframe控件 # 目前126和163登陸的iframe的id也是動態變化,因此不能用id定位iframe iframe = world.driver.find_element_by_xpath('//iframe[contains(@id,"x-URS-iframe")]') world.driver.switch_to.frame(iframe) # 獲取用戶名輸入框 userName = world.driver.find_element_by_xpath("//input[@name='email']") userName.clear() #輸入用戶名 userName.send_keys(username) # 獲取密碼輸入框 pwd = world.driver.find_element_by_xpath("//input[@name='password']") # 輸入密碼 pwd.send_keys(password) #發送一個回車鍵 pwd.send_keys(Keys.RETURN) #等待15秒,以便登陸後成功進入登陸頁面 time.sleep(15) # 退出iframe world.driver.switch_to.default_content() @step(u'頁面會出現"(.*)"關鍵字') def message_dispalyed_Login_Successfully(step,keywords): #斷言登陸成功後,頁面是否出現預期的關鍵字 assert keywords in world.driver.page_source #斷言成功後,打印登陸成功信息 print "Login Success" @step(u'用戶從頁面單擊退出連接') def LogOut_from_the_Application(step): print "==============",world.driver #單擊退出按鈕,退出登陸 world.driver.find_element_by_link_text(u"退出").click() time.sleep(3) @step(u'頁面顯示"(.*)"關鍵內容') def dispalyed_logOut_Successfully(step,keywords): #斷言退出登陸後,頁面是否出現退出成功關鍵內容 assert keywords in world.driver.page_source print u"Logout Success" #退出瀏覽器 world.driver.quit()
Login_English.feature文件內容以下:
#encoding=utf-8 Feature: login and logout Scenario: Successful Login with Valid Credentials Given Launch a browser When User visit to http://mail.126.com Page And User enters UserName"chenyl_2019" and Password"sxzycyl0910" Then Message displayed Login Successfully Scenario: Successful LogOut When User LogOut from the Application Then Message displayed LogOut Successfully
Login_English.py文件內容以下:
#encoding=utf-8 from lettuce import * from selenium import webdriver from selenium.webdriver.common.keys import Keys import time @step('Launch a browser') def open_browser(step): try: #建立Chrome瀏覽器的driver實例,並在於全局對象world中,供後續場景或步驟函數使用 world.driver = webdriver.Firefox(executable_path = "D:\\geckodriver") #瀏覽器窗口最大化 world.driver.maximize_window() time.sleep(3) except Exception,e: raise e @step('User visit to (.*) Page') def visit_url(step,url): world.driver.get(url) @step('User enters Username "(.*)" and Password "(.*)"') def user_enters_UserName_and_Password(step,username,password): print username,password # 點擊切換成帳號密碼登陸 world.driver.find_element_by_id("lbNormal").click() time.sleep(1) # 找到並切換進iframe控件 # 目前126和163登陸的iframe的id也是動態變化,因此不能用id定位iframe iframe = world.driver.find_element_by_xpath('//iframe[contains(@id,"x-URS-iframe")]') world.driver.switch_to.frame(iframe) # 獲取用戶名輸入框 userName = world.driver.find_element_by_xpath("//input[@name='email']") userName.clear() #輸入用戶名 userName.send_keys(username) # 獲取密碼輸入框 pwd = world.driver.find_element_by_xpath("//input[@name='password']") # 輸入密碼 pwd.send_keys(password) #發送一個回車鍵 pwd.send_keys(Keys.RETURN) #等待15秒,以便登陸後成功進入登陸頁面 time.sleep(15) # 退出iframe world.driver.switch_to.default_content() @step('Message displayed Login Successfully') def message_dispalyed_Login_Successfully(step,keywords): #斷言登陸成功後,頁面是否出現預期的關鍵字 assert u"未讀郵件" in world.driver.page_source #斷言成功後,打印登陸成功信息 print "Login Success" @step('User LogOut from the Application') def LogOut_from_the_Application(step): print "==============",world.driver #單擊退出按鈕,退出登陸 world.driver.find_element_by_link_text(u"退出").click() time.sleep(3) @step('Message displayed LogOut Successfully') def dispalyed_logOut_Successfully(step,keywords): #斷言退出登陸後,頁面是否出現退出成功關鍵內容 assert u"您已成功退出網易郵箱" in world.driver.page_source print u"Logout Success" #退出瀏覽器 world.driver.quit()
terrain.py文件內容用於統計各個場景執行結果信息,具體內容以下:
#encoding=utf-8 from lettuce import * #在全部場景執行前執行 @before.all def say_hello(): print u"開始執行行爲數據驅動測試..." #在每一個Scenario開始執行前執行 @before.each_scenario def setuo_some_scenario(scenario): print u"開始執行場景:'%s'" % scenario.name #在每一個Scenario執行結束後執行 @after.each_scenario def teardown_some_scenario(scenario): print u"場景:'%s'執行結束" % scenario.name #在全部場景執行結束後執行 @after.all #默認獲取執行結果的對象做爲total參數 def say_goodbye(total): result = u"恭喜,%d個場景被運行,%d個場景運行成功" % ( total.scenarios_ran, #一共多少場景運行了 total.scenarios_passed #一共多少場景運行成功了 ) print result
九、解決中文描述的場景輸出到控制檯亂碼在默認編碼爲GBK的Windows系統中執行場景使用中文描述的行爲驅動測試時,打印到控制檯的場景等信息,中文會出現亂碼,這是因爲lettuce框架將輸出到控制檯的場景描述信息轉成UTF8編碼的字符致使的。下面針對lettuce(0.2.23)版本給出具體解決方法。 (1)進入Python安裝目錄中lettuce安裝路徑中的plugins目錄中, 好比本地路徑爲C:\Python27\Lib\site-packages\lettuce\plugins。 (2)找到該目錄下的colored_shell_output.py文件, (3)打開該文件,找到該文件的第32行代碼what = what.encode('utf-8') ,將其改爲what = what#.encode('utf-8') 或者刪除(註釋)