行为驱动(2)
行为驱动(2)
5、lettuce框架的步骤数据表格
实例:
(1)在PyCharm工具中创建如下所示的目录结构及文件
..\lettuce\StepDataTables\features\student.features
..\lettuce\StepDataTables\features\steps.py
(2)student.feature文件具体内容如下 所示:
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文件中的数据,并提供后续操作,具体内容如下:
#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]
6、 使用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文件用于存放数据驱动所需要的数据,具体内容如下:
: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!-------------"
7、使用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
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
9、解决中文描述的场景输出到控制台乱码
在默认编码为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') 或者删除(注释)