行为驱动(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') 或者删除(注释)

posted @ 2019-06-27 21:23  测试小子  阅读(358)  评论(0编辑  收藏  举报