安裝行為驅動模塊lettuce(卷心菜)模塊
pip install lettuce
Successfully installed argparse-1.4.0 colorama-0.3.9 extras-1.0.0 fixtures-3.0.0 funcsigs-1.0.2 fuzzywuzzy-0.16.0 lettuce-0.2.23 linecache2-1.0.0 mock-2.0.0 pbr-4.1.0 python-mimeparse-1.6.0 python-subunit-1.3.0 sure-1.4.11 testtools-2.3.0 traceback2-1.4.0 unittest2-1.1.0
官網:http://lettuce.it
下面通過第一個例子來說明行為驅動是做什么的。
第一個例子,解釋行為驅動的用法和結構,用的是方法
建一個目錄(名稱隨意),在該目錄下新建features目錄,在features目錄下,建兩個文件steps.py和zero.feature
features目錄名稱是固定的 feature文件的后綴名必須是xxx.feature,也是固定的。
features和features下這兩個文件就是行為驅動lettuce的結構,一個是文本文件,用來定義用例是什么,另一個是真正執行用例步驟的腳本
可以對比一下zero.feature里用例的三個關鍵字語句和steps的三個函數的映射關系,就是一一對應的,一個語句對應一個函數,這就是行為驅動的結構。
總結一下行為驅動:
行為驅動就是通過feature文件里關鍵字對應的自然語言映射腳本中對應的函數,然后執行steps腳本來執行feature中描述的測試用例。
每一句自然語言的描述都會有一個函數或步驟執行跟它對應,這么做的意義是在執行用例的時候可以看到他在干什么,看他完成了多少需求,因為自然語言中描述的都是需求中定義的東西,行為驅動一般定義在敏捷開發當中,敏捷開發中有數據驅動、行為驅動、驗收測試驅動,這就是行為驅動,好處是可以驗證需求是否都完成了,可以在開發完成后執行行為驅動用例,把完成的需求按自然語言方式輸出出來,都輸出了就說明完成了,就是把測試用例和自然語言的需求之間建立一個聯系,當他執行通過之后,我就知道我的需求完成了多少,就是做了一個映射的關系,這樣需求人員就知道原來我的進度是這樣,在寫測試用例的時候,需求人員自己就可以寫了,把不同的場景寫出來就可以了,不用管測試用例的實現,最后執行的時候就知道需求完成了多少,因為只要測試用例跑通過了就認為是成功了,這就是行為驅動的好處,方便非測試、非技術人員對需求進度的了解。
本質就是把自然語言跟你的測試步驟做了一一對應,以此來看進度的完成情況,以及是否通過測試,一般認為通過測試的才認為開發算完成了,在敏捷測試中用的比較多,在一般正常的非敏捷的測試中用的不多。
可以在githun上找源碼看一下lettuce怎么實現的。
如何執行測試用例:
到features目錄的上層目錄中,打開cmd,執行lettuce
綠色代表執行通過,有問題的話會報錯。
zero.feature:
#說明功能是干什么的,用的時候把注釋都去掉,防止受影響
#Feature,Scenario,Given,When,Then等第一個字母大寫的,這些都是關鍵字,不能變
Feature: Compute factorial#計算階乘
#備注,描述說明
In order to play with Lettuce
As beginners
We'll implement factorial
#場景1,就是第一個測試用例
Scenario: Factorial of 0#0的階乘
#下面這三個自然語言的描述都有一個函數跟它對應,執行測試用例的時候可以看他干了什么
#是否正確的執行
#敏捷里也有驗收測試驅動,行為驅動,數據驅動
#把測試用例用自然語言的描述和程序有一個映射
Given I have the number 0#如果我有一個數是0
When I compute its factorial#當我計算這個階乘的時候
Then I see the number 1#計算完了之后,我得到的結果0的階乘是1
#下面以此類推,原理都是一樣的
Scenario: Factorial of 1
Given I have the number 1
When I compute its factorial
Then I see the number 1
Scenario: Factorial of 2
Given I have the number 2
When I compute its factorial
Then I see the number 2
Scenario: Factorial of 3
Given I have the number 3
When I compute its factorial
Then I see the number 6
steps.py:定義方法,實現測試用例
#encoding=utf-8
from lettuce import *
# 用於計算整數的階乘函數
def factorial(number):#被測試對象,告訴怎么計算階乘的
number = int(number)
if (number == 0) or (number == 1):
return 1
else:
return reduce(lambda x, y: x * y, range(1, number + 1))#算階乘的方法,后續自己演練#一下
#可以對比一下zero.feature里用例的三個關鍵字語句和下面的三個函數的映射關系,就是一#一對應的,一個語句對應一個函數,這就是行為驅動
#下面的@是裝飾器的用法,括號里是給裝飾器穿的參數
@step('I have the number (\d+)')#跟feature里的Given所指的一樣
def have_the_number(step, number):
# 將通過正則表達式匹配的數字存於全局變量world中
# world是lettuce里的命名空間,這命名空間里的所有變量在所有lettuce方法中共用,類似全局變量
#做了一個類型轉換
world.number = int(number)#world是lettuce里的命名空間,這空間里的所有變量在所有lettuce里公用
@step('I compute its factorial')#同步features文件,然后用正則
def compute_its_factorial(step):
# 從全局變量world中取出匹配的數字,
# 計算其階乘,並將結果再存回world中
world.number = factorial(world.number)#把world.number傳給函數,返回后又傳給自己了
@step('I see the number (\d+)')
def check_number(step, expected):#step對應I see the number ,expected對應分組(\d+)
# 通過正則匹配到預期數字
expected = int(expected)#把字符串轉成整形
# 斷言計算階乘結果是否等於預期
assert world.number == expected, "Got %d" %world.number
測試用例的執行:
到features目錄的上層目錄中,打開cmd,執行lettuce
綠色代表執行通過,有問題的話會報錯。
結果:
D:\test\xingweiqudong\practice1>lettuce
c:\python27\lib\site-packages\fuzzywuzzy\fuzz.py:35: UserWarning: Using slow pure-python SequenceMatcher. Install python-Levenshtein to remove this warning
warnings.warn('Using slow pure-python SequenceMatcher. Install python-Levenshtein to remove this warning')
Feature: Compute factorial # \features\zero.feature:1
In order to play with Lettuce # \features\zero.feature:2
As beginners # \features\zero.feature:3
We'll implement factorial # \features\zero.feature:4
Scenario: Factorial of 0 # \features\zero.feature:6
Given I have the number 0 # \features\steps.py:13
When I compute its factorial # \features\steps.py:18
Then I see the number 1 # \features\steps.py:24
Scenario: Factorial of 1 # \features\zero.feature:11
Given I have the number 1 # \features\steps.py:13
When I compute its factorial # \features\steps.py:18
Then I see the number 1 # \features\steps.py:24
Scenario: Factorial of 2 # \features\zero.feature:16
Given I have the number 2 # \features\steps.py:13
When I compute its factorial # \features\steps.py:18
Then I see the number 2 # \features\steps.py:24
Scenario: Factorial of 3 # \features\zero.feature:21
Given I have the number 3 # \features\steps.py:13
When I compute its factorial # \features\steps.py:18
Then I see the number 6 # \features\steps.py:24
1 feature (1 passed)
4 scenarios (4 passed)
12 steps (12 passed)

第二個例子:用類的方式實現
features沒有變化和第一個例子一樣,steps文件有變化,用類的方式實現
方法就是在類的上邊用裝飾器@steps來實現
steps.py:
#encoding=utf-8
from lettuce import world, steps
def factorial(number):
number = int(number)
if (number == 0) or (number == 1):
return 1
else:
return reduce(lambda x, y: x * y, range(1, number + 1))
@steps#裝飾器,測試步驟類,他的每個方法默認都是一個測試步驟
class FactorialSteps(object):
"""Methods in exclude or starting with _ will not be considered as step"""
exclude = ['set_number', 'get_number']#不作為測試步驟,因為@steps測試步驟類里的方#法都作為測試步驟,所以在這里做一個排除
def __init__(self, environs):
# 初始全局變量
self.environs = environs#實例化后就是一個world,存全局共享變量的對象
def set_number(self, value):#不是步驟
# 設置全局變量中的number變量的值
#設定測試數據
self.environs.number = int(value)#就是world存到了這個實例變量里
def get_number(self):#不是步驟
# 從全局變量中取出number的值
return self.environs.number#取出測試數據
#私有方法默認也不是測試步驟
def _assert_number_is(self, expected, msg="Got %d"):
number = self.get_number()
# 斷言取出的數據和期望的數據是否一致
assert number == expected, msg % number
#下邊三個都是測試步驟
def have_the_number(self, step, number):
#下邊跟自然語言描述是一樣的,三句話
'''I have the number (\d+)'''#從feature文件里取出的自然語言的內容
# 上面的三引號引起的代碼必須寫,並且必須是三引號引起
# 表示從場景步驟中獲取需要的數據
# 並將獲得數據存到環境變量number中
self.set_number(number)#獲取一下測試數據
def i_compute_its_factorial(self, step):
"""When I compute its factorial"""
number = self.get_number()#取出數據
# 調用factorial方法進行階乘結算,
# 並將結算結果存於全局變量中的number中
self.set_number(factorial(number))#通過測試數據調用被測試方法
def check_number(self, step, expected):
'''I see the number (\d+)'''
# 上面的三引號引起的代碼必須寫,並且必須是三引號引起
# 表示從場景步驟中獲取需要的數據以便斷言測試結果
self._assert_number_is(int(expected))#私有方法做一個斷言
FactorialSteps(world)#測試類的實例化,world是一個類,lettuce里可以存全局共享變量的對象
Zero.eature:
Feature: Compute factorial
In order to play with Lettuce
As beginners
We'll implement factorial
Scenario: Factorial of 0
Given I have the number 0
When I compute its factorial
Then I see the number 1
Scenario: Factorial of 1
Given I have the number 1
When I compute its factorial
Then I see the number 1
Scenario: Factorial of 2
Given I have the number 2
When I compute its factorial
Then I see the number 2
Scenario: Factorial of 3
Given I have the number 3
When I compute its factorial
Then I see the number 6
結果:
D:\test\xingweiqudong\practice2>lettuce
c:\python27\lib\site-packages\fuzzywuzzy\fuzz.py:35: UserWarning: Using slow pure-python SequenceMatcher. Install python-Levenshtein to remove this warning
warnings.warn('Using slow pure-python SequenceMatcher. Install python-Levenshtein to remove this warning')
Feature: Compute factorial # \features\zero.feature:1
In order to play with Lettuce # \features\zero.feature:2
As beginners # \features\zero.feature:3
We'll implement factorial # \features\zero.feature:4
Scenario: Factorial of 0 # \features\zero.feature:6
Given I have the number 0 # \features\steps.py:35
When I compute its factorial # \features\steps.py:42
Then I see the number 1 # \features\steps.py:49
Scenario: Factorial of 1 # \features\zero.feature:11
Given I have the number 1 # \features\steps.py:35
When I compute its factorial # \features\steps.py:42
Then I see the number 1 # \features\steps.py:49
Scenario: Factorial of 2 # \features\zero.feature:16
Given I have the number 2 # \features\steps.py:35
When I compute its factorial # \features\steps.py:42
Then I see the number 2 # \features\steps.py:49
Scenario: Factorial of 3 # \features\zero.feature:21
Given I have the number 3 # \features\steps.py:35
When I compute its factorial # \features\steps.py:42
Then I see the number 6 # \features\steps.py:49
1 feature (1 passed)
4 scenarios (4 passed)
12 steps (12 passed)

第三個例子:用表格的模式
用表格的模式,用多行數據的時候會用到
核心的原理:表格里的數據是
Student.feature:多了個and關鍵字
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 |
#只想讓以G開頭的人付款
When I bill names starting with "G"#當我打了一個G
Then I see those billed students:#看到這些人,以G開頭的
| name | monthly_due | billed |
| Gabriel | $ 300 | no |
| Gloria | $ 442.65 | no |
And those that weren't:#且不看到這些人(非G開頭)的信息
| name | monthly_due | billed |
| Anton | $ 500 | no |
| Jack | $ 400 | no |
| Ken | $ 907.86 | no |
| Leonard | $ 742.84 | no |
step.py:
#encoding=utf-8
from lettuce import *
@step('I have the following students in my database:')#以函數的方式實現的
def students_in_database(step):
if step.hashes:#feature的數據表的內容給讀出來
# 如果存在步驟表格數據,則繼續后續步驟
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#給全局變量
#打印了一下取出來的東西,'feature里bill names starting with部分沒哈希表
print "no data exist:",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:#就是feature里those that weren’t部分的數據表/哈希表
# 斷言學生名字不以world.startAlpha變量存取的的字母開頭
assert world.startAlpha not in j["name"][0]#斷言表里每個名字的首字母不是G
結果:
D:\test\xingweiqudong\practice3>lettuce
Feature: bill students alphabetically # \features\student.feature:1
In order to bill students properly # \features\student.feature:2
As a financial specialist # \features\student.feature:3
I want to bill those which name starts with some letter # \features\student.feature:4
Scenario: Bill students which name starts with "G" # \features\student.feature:6
Given I have the following students in my database: # \features\step.py:5
Given I have the following students in my database: # \features\step.py:5
| 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" # \features\step.py:43
When I bill names starting with "G" # \features\step.py:43
Then I see those billed students: # \features\step.py:50
| name | monthly_due | billed |
| Gabriel | $ 300 | no |
| Gloria | $ 442.65 | no |
And those that weren't: # \features\step.py:57
| name | monthly_due | billed |
| Anton | $ 500 | no |
| Jack | $ 400 | no |
| Ken | $ 907.86 | no |
| Leonard | $ 742.84 | no |
1 feature (1 passed)
1 scenario (1 passed)
4 steps (4 passed)

py -3 m pip install lettuce python 3,運行python3
virtual env 虛擬環境,生成獨立的沙盒,跟虛擬機一樣,生成了一個虛擬的python運行環境
帖子:linux下的
https://blog.csdn.net/u012734441/article/details/55044025/
pycharm里新建項目時,選virtualEnv,安裝的python編譯環境跟操作系統就完全獨立的
window下
https://www.cnblogs.com/ruhai/p/6597280.html
第四個例子,基於webdriver,數據驅動的例子
sogou.feature:
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>"#尖括號是保留字,對應Examples下的數據列
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是關鍵字
sougou.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 = "c:\\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()
terrain.py:
#本質是在用例開始的前、后,每個場景之前、后,每個步驟開始之前、后做一些事情
#跟testNG的標簽差不多,就是一個框架
#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 = 'e:/BddDataDriveRreport.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..."
# 在每個secnario開始執行前執行
@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, defined at %s" % (
step.sentence, # 執行的步驟
step.defined_at.file # 步驟定義在哪個文件
)
# 將每個場景的每一步信息打印到日志
logging.info(run)
# 每個step執行后執行
@after.each_step
def teardown_some_step(step):
if not step.hashes:#判斷是否沒有表格文件
print "no tables in the step" #會被打印9次,因為我們僅僅設定了場景的table,而不是給每個步驟設定table
#注意每個step可以有自己的table,等價於把table的數據作為一個字典傳入到程序中使用。
logging.info("no tables in the step")#打印沒有table
# 在每個secnario執行結束執行,每一個場景結束后打印
@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! ------"
結果:
D:\test\xingweiqudong\practice4>lettuce
Lettuce will start to run tests right now...
Lettuce will start to run tests right now...
Feature: Search in Sogou website # \features\sogou.feature:1
In order to Search in Sogou website # \features\sogou.feature:2
As a visitor # \features\sogou.feature:3
We'll search the NBA best player # \features\sogou.feature:4
Begin to execute scenario name:Search NBA player
Scenario Outline: Search NBA player # \features\sogou.feature:6
Begin to execute scenario name:Search NBA player
Given I have the english name "<search_name>" # \features\sogou.py:7
Jordan
no tables in the step
no tables in the step
When I search it in Sogou website # \features\sogou.py:12
no tables in the step
no tables in the step
Then I see the entire name "<search_result>" # \features\sogou.py:20
no tables in the step
no tables in the step
Examples:
| search_name | search_result |
| Jordan | Michael |
Curry
no tables in the step
no tables in the step
no tables in the step
no tables in the step
no tables in the step
no tables in the step
| Curry | Stephen |
Kobe
no tables in the step
no tables in the step
no tables in the step
no tables in the step
no tables in the step
no tables in the step
| Kobe | Bryant |
finished, scenario name:Search NBA player
finished, scenario name:Search NBA player
Congratulations, 3 of 3 scenarios passed!
------ Goodbye! ------
1 feature (1 passed)
3 scenarios (3 passed)
9 steps (9 passed)
Congratulations, 3 of 3 scenarios passed!
------ Goodbye! ------

第五個例子:feature里用中文#language:zh-CN
涉及中文,要改個東西,colored_shell_output.py里encode('utf-8')注釋掉
在默認編碼為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')
Baidu.feature:
#encoding=utf-8
# language: zh-CN
特性: 在百度網址搜索IT相關書籍
能夠搜索到書的作者,比如吳曉華
場景: 在百度網站搜索IT相關書籍
如果將搜索詞設定為書的名字"<書名>"
當打開百度網站
和在搜索輸入框中輸入搜索的關鍵詞,並點擊搜索按鈕后
那么在搜索結果中可以看到書的作者"<作者>"
例如:
| 書名 | 作者 |
| Selenium WebDriver實戰寶典 | 吳曉華 |
| HTTP權威指南 | 協議 |
| Python核心編程 | Python |
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 = 'e:/BddDataDriveRreport.log',
# 打開日志文件的方式
filemode = 'w'
)
terranin.py內容跟上個例子一樣
#encoding=utf-8
from lettuce import *
from log import *
# 在所有場景執行前執行
@before.all
def say_hello():
logging.info(u"開始執行行為數據驅動測試...")
# 在每個secnario開始執行前執行
@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)
# 在每個secnario執行結束執行
@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"本次行為數據驅動執行結束")
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 = "c:\\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, "got word:%s" %searchResult
world.driver.quit()
結果:
D:\test\xingweiqudong\practice5>lettuce
特性: 在百度網址搜索IT相關書籍 # \features\baidu.feature:4
能夠搜索到書的作者,比如吳曉華 # \features\baidu.feature:5
場景模板: 在百度網站搜索IT相關書籍 # \features\baidu.feature:7
如果將搜索詞設定為書的名字"<書名>" # \features\baidu.py:8
Selenium WebDriver實戰寶典
當打開百度網站 # \features\baidu.py:13
和在搜索輸入框中輸入搜索的關鍵詞,並點擊搜索按鈕后 # \features\baidu.py:18
那么在搜索結果中可以看到書的作者"<作者>" # \features\baidu.py:24
例如:
| 書名 | 作者 |
| Selenium WebDriver實戰寶典 | 吳曉華 |
HTTP權威指南
| HTTP權威指南 | 協議 |
Python核心編程
| Python核心編程 | Python |
1 feature (1 passed)
3 scenarios (3 passed)
12 steps (12 passed)

第六個例子:批量執行
把多個feature和腳本文件放在一個目錄下,lettuce會自動的匹配feature的東西,一個一個的執行
Login_Chinese.feature:
#encoding=utf-8
# language: zh-CN
特性: 登錄126郵箱和退出126郵箱登錄
場景: 成功登錄126郵箱
假如啟動一個瀏覽器
當用戶訪問http://mail.126.com網址
當用戶輸入輸入用戶名“testman1980”和密碼“wulaoshi1978”
那么頁面會出現“未讀郵件”關鍵字
場景: 成功退出126郵箱
當用戶從頁面單擊退出鏈接
那么頁面顯示“您已成功退出網易郵箱”關鍵內容
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"testman1980" and Password"wulaoshi1978"
Then Message displayed Login Successfully
Scenario: Successful LogOut
When User LogOut from the Application
Then Message displayed LogOut Successfully
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="c:\\geckodriver")
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.maximize_window()
time.sleep(3)
# 切換進frame控件
world.driver.switch_to.frame("x-URS-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)
@step(u'頁面會出現“(.*)”關鍵字')
def message_displayed_Login_Successfully(step, keywords):
# print world.driver.page_source.encode('utf-8')
# 斷言登錄成功后,頁面是否出現預期的關鍵字
world.driver.switch_to.default_content()#自己加的
assert keywords in world.driver.page_source
# 斷言成功后,打印登錄成功信息
print "Login Success"
@step(u'用戶從頁面單擊退出鏈接')
def LogOut_from_the_Application(step):
print "====",world.driver
# time.sleep(5)
world.driver.switch_to.default_content()#自己加的
# 點擊退出按鈕,退出登錄
world.driver.find_element_by_link_text(u"退出").click()
time.sleep(8)
@step(u'頁面顯示“(.*)”關鍵內容')
def displayed_LogOut_Successfully(step, keywords):
# 斷言退出登錄后,頁面是否出現退出成功關鍵內容
assert keywords in world.driver.page_source
print u"Logout Success"
# 退出瀏覽器
world.driver.quit()
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:
world.driver = webdriver.Firefox(executable_path="c:\\geckodriver")
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):
world.driver.maximize_window()
time.sleep(3)
world.driver.switch_to.frame("x-URS-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)
time.sleep(15)
@step('Message displayed Login Successfully')
def message_displayed_Login_Successfully(step):
# print world.driver.page_source.encode('utf-8')
world.driver.switch_to.default_content()#自己加的
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
# time.sleep(15)
world.driver.switch_to.default_content()#自己加的
world.driver.find_element_by_link_text(u"退出").click()
time.sleep(4)
@step('Message displayed LogOut Successfully')
def displayed_LogOut_Successfully(step):
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"開始執行行為數據驅動測試..."
# 在每個secnario開始執行前執行
@before.each_scenario
def setup_some_scenario(scenario):
print u'開始執行場景“%s”' %scenario.name
# 在每個secnario執行結束后執行
@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
結果:
D:\test\xingweiqudong\practice6>lettuce
開始執行行為數據驅動測試...
特性: 登錄126郵箱和退出126郵箱登錄 # \features\Login_Chinese.feature:4
場景: 成功登錄126郵箱 # \features\Login_Chinese.feature:6
開始執行場景“成功登錄126郵箱”
假如啟動一個瀏覽器 # \features\Login_Chinese.py:9
當用戶訪問http://mail.126.com網址 # \features\Login_Chinese.py:19
當用戶訪問http://mail.126.com網址 # \features\Login_Chinese.py:19
當用戶輸入輸入用戶名“xiaxiaoxu1987”和密碼“gloryroad” # \features\Login_Chinese.py:24
當用戶輸入輸入用戶名“xiaxiaoxu1987”和密碼“gloryroad” # \features\Login_Chinese.py:24
那么頁面會出現“未讀郵件”關鍵字 # \features\Login_Chinese.py:46
那么頁面會出現“未讀郵件”關鍵字 # \features\Login_Chinese.py:46
場景“成功登錄126郵箱”執行結束
場景: 成功退出126郵箱 # \features\Login_Chinese.feature:12
開始執行場景“成功退出126郵箱”
當用戶從頁面單擊退出鏈接 # \features\Login_Chinese.py:55
當用戶從頁面單擊退出鏈接 # \features\Login_Chinese.py:55364d904c9c4")>
那么頁面顯示“您已成功退出網易郵箱”關鍵內容 # \features\Login_Chinese.py:64
那么頁面顯示“您已成功退出網易郵箱”關鍵內容 # \features\Login_Chinese.py:64
場景“成功退出126郵箱”執行結束
Feature: login and logout # \features\Login_English.feature:3
Scenario: Successful Login with Valid Credentials # \features\Login_English.feature:5
開始執行場景“Successful Login with Valid Credentials”
Given Launch a browser # \features\Login_English.py:8
When User visit to http://mail.126.com Page # \features\Login_English.py:16
And User enters UserName"xiaxiaoxu1987" and Password"gloryroad" # \features\Login_English.py:20
Then Message displayed Login Successfully # \features\Login_English.py:33
Then Message displayed Login Successfully # \features\Login_English.py:33
場景“Successful Login with Valid Credentials”執行結束
Scenario: Successful LogOut # \features\Login_English.feature:11
開始執行場景“Successful LogOut”
When User LogOut from the Application # \features\Login_English.py:40
When User LogOut from the Application # \features\Login_English.py:40")>
Then Message displayed LogOut Successfully # \features\Login_English.py:48
Then Message displayed LogOut Successfully # \features\Login_English.py:48
場景“Successful LogOut”執行結束
2 features (2 passed)
4 scenarios (4 passed)
12 steps (12 passed)
恭喜,4個場景被運行,4個場景運行成功

pip show lettuce 顯示lettuce版本號
練習,用lettuce實現兩個數的相加
steps.py:
#encoding=utf-8
from lettuce import *
# 用於計算整數的階乘函數
def sum(a,b):
return a+b
@step('I have the number (\d),(\d)')
def have_the_number(step, number1,number2):
# 將通過正則表達式匹配的數字存於全局變量world中
world.number1 = int(number1)
world.number2 = int(number2)
print "number1:",number1#這樣打印顯示不了
print "number2:",number2#這樣打印顯示不了
@step('I compute its sum')
def compute_its_sum(step):
# 從全局變量world中取出匹配的數字,
# 計算其階乘,並將結果再存回world中
world.number = sum(world.number1,world.number2)
print "world.number:",world.number#這樣打印顯示不了
@step('I see the number (\d+)')
def check_number(step, expected):
# 通過正則匹配到預期數字
expected = int(expected)
print "expected:",expected#這樣打印顯示不了
# 斷言計算階乘結果是否等於預期
assert world.number == expected, "Got %d" %world.number
feature:
Feature: Compute sum
In order to play with Lettuce
As beginners
We'll implement sum
Scenario: sum of 1,2
Given I have the number 1,2
When I compute its sum
Then I see the number 3
結果:
D:\test\xingweiqudong\test>lettuce
Feature: Compute sum # \features\zero.feature:1
In order to play with Lettuce # \features\zero.feature:2
As beginners # \features\zero.feature:3
We'll implement sum # \features\zero.feature:4
Scenario: sum of 1,2 # \features\zero.feature:6
Given I have the number 1,2 # \features\steps.py:9
number1: 1
Given I have the number 1,2 # \features\steps.py:9
When I compute its sum # \features\steps.py:17
When I compute its sum # \features\steps.py:17
Then I see the number 3 # \features\steps.py:24
Then I see the number 3 # \features\steps.py:24
1 feature (1 passed)
1 scenario (1 passed)
3 steps (3 passed)

