python behave學習筆記 - 基礎

基本概念

behave要被執(zhí)行缴守,需要運行在滿足下面兩種情況的目錄下

  1. 有feature files。這個feature files可以試由非技術人員編寫
  2. 一個“steps”目錄铝穷,steps里面包含python step implementation

還可以添加一些environmental controls。 比如

before
after
scenarios
features

一個可執(zhí)行的最小feature目錄為

features/
features/everything.feature
features/steps/
features/steps/steps.py

最小為一個features目錄 下面一個feature文件和一個steps目錄佳魔。steps里面包含了測試代碼曙聂。

一個更復雜的目錄為

features/
features/signup.feature
features/login.feature
features/account_details.feature
features/environment.py
features/steps/
features/steps/website.py
features/steps/utils.py

按照項目的不同模塊有不同的feature files,也有environment.py這樣的環(huán)境配置文件鞠鲜,在steps下面也有測試代碼

在命令行執(zhí)行behave即可執(zhí)行所有測試宁脊,在console能看到測試結果

什么是feature files

feature file是指一個通常命名為 **.feature的純文本文件(UTF-8)。這個文件里面包含了用自然語言(Gherkin)描述的系統(tǒng)的功能特征贤姆。這些功能特征是具有代表性的期望結果榆苞。

Feature: Fight or flight
  In order to increase the ninja survival rate,
  As a ninja commander
  I want my ninjas to decide whether to take on an
  opponent based on their skill levels

  Scenario: Weaker opponent
    Given the ninja has a third level black-belt
    When attacked by a samurai
    Then the ninja should engage the opponent

  Scenario: Stronger opponent
    Given the ninja has a third level black-belt
    When attacked by Chuck Norris
    Then the ninja should run for his life

使用Gherkin來描述,具有以下特點:

  • 結構是 feature下面有多個scenarios霞捡,scenarios下面是 Given When Then的表述方法
  • feature的描述用的是 in order to ….. as a ….. I want …….
  • behave會采用 Given When Then的步驟描述來map到測試代碼中對應的相同步驟描述坐漏。Given When Then是真實的執(zhí)行步驟
  • Given: 在用戶或是外部系統(tǒng)對應用進行交互之前,我們要把系統(tǒng)處于一個已知的狀態(tài)。這個更加明確測試執(zhí)行的前提條件和所要求的系統(tǒng)狀態(tài)赊琳。要避免在GIVEN中涉及到用戶交互
  • When:用戶或是外部系統(tǒng)所采取的與待測試系統(tǒng)的交互步驟街夭。這個交互能改變待測試系統(tǒng)的狀態(tài)
  • Then: 待觀察的結果或是期望結果
  • 除了Given When Then,我們還可以使用And或是But來做為步驟從而進行步驟描述的擴展躏筏。
Scenario: Stronger opponent
  Given the ninja has a third level black-belt
   When attacked by Chuck Norris
   Then the ninja should run for his life
   And fall off a cliff

Scenarios Outlines來實現數據驅動

在behave實現數據驅動測試可以使用Scenario Outline這個關鍵字配合Examples這個關鍵字使用板丽。 不同的數據會在相同的方法中執(zhí)行。

Scenario Outline: Blenders
   Given I put <thing> in a blender,
    when I switch the blender on
    then it should transform into <other thing>

 Examples: Amphibians
   | thing         | other thing |
   | Red Tree Frog | mush        |

 Examples: Consumer Electronics
   | thing         | other thing |
   | iPhone        | toxic waste |
   | Galaxy Nexus  | toxic waste |

上面的例子中 使用了Scenario Outline來定義scenario 在GIVEN中使用 <varible name>來定義數據變量趁尼。
例子中有兩個變量 <thing><other thing> 這兩個變量名會在Examples中變?yōu)閠able的heading埃碱。Examples中的表格數據就是傳入方法的數據。

behave會運行表格中的每一行酥泞,每一行就代表著一個場景

Step Data 步驟數據

behave支持對feature文件中的step增加描述(context.text)和表格(context.table)來增加對feature的描述以及實現數據驅動

描述 Context.text : 在step后用兩個“”“包含的文本

Scenario: some scenario
  Given a sample text loaded into the frobulator
     """
     Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
     eiusmod tempor incididunt ut labore et dolore magna aliqua.
     """
 When we activate the frobulator
 Then we will find it similar to English

這個文本可以在測試代碼中使用Context.text來調用

表格**Context.table**: 在step后縮進表示的數據表格

Scenario: some scenario
  Given a set of specific users
     | name      | department  |
     | Barry     | Beer Cans   |
     | Pudey     | Silly Walks |
     | Two-Lumps | Silly Walks |

 When we count the number of people in each department
 Then we will find two people in "Silly Walks"
  But we will find one person in "Beer Cans"

這個表格的數據可以再測試代碼中使用Context.table來調用砚殿,然后加到model里面。下面是example

@given('a set of specific users')
def step_impl(context):
    for row in context.table:
        model.add_user(name=row['name'], department=row['department'])

測試代碼 - Python Step Implementations

測試代碼實現在python文件中芝囤,這些python文件都需要被放入到”steps“文件夾下瓮具。測試代碼的文件名并不需要與feature文件的名稱一致。

Steps是通過修飾符來進行匹配的凡人。在測試代碼中名党,修飾符接受一串字符串,這串字符串要和feature文件中scenario使用的字符串一樣

feature文件中描述如下

Scenario: Search for an account
   Given I search for a valid account
  Then I will see the account details

則測試代碼文件中應該如下

@given('I search for a valid account')
def step_impl(context):
   context.browser.get('http://localhost:8000/index')
   form = get_element(context.browser, tag='form')
   get_element(form, name="msisdn").send_keys('61415551234')
   form.submit()

@then('I will see the account details')
def step_impl(context):
   elements = find_elements(context.browser, id='no-account')
   eq_(elements, [], 'account not found')
   h = get_element(context.browser, id='account-head')
   ok_(h.text.startswith("Account 61415551234"),
       'Heading %r has wrong text' % h.text)

given when then 都不是必須的挠轴,都可以根據情況來使用

step修飾符即是 @given @then @when

”and“和”but“會被當做when/given/then的一部分來執(zhí)行传睹。比如一個and屬于given step下,那么and這個step就會變?yōu)間iven的一部分

如果你希望你的step能夠invoke另外一個step岸晦,可以使用Context.execute_steps()

@when('I do the same thing as before')
def step_impl(context):
    context.execute_steps('''
        when I press the big red button
         and I duck
    ''')

”I do the same thing as before“ step會執(zhí)行另外兩個step

Step Parameters

在feature文件中step的描述可以包含參數 step parameter以方便重用

比如下面的scenario中 Then都是使用了差不多的描述

Scenario: look up a book
  Given I search for a valid book
  Then the result page will include "success"

Scenario: look up an invalid book
  Given I search for a invalid book
  Then the result page will include "failure"

唯一的不同就是 “success” 和 “failure”欧啤。這個可以使用測試代碼來處理

@then('the result page will include "{text}"')
def step_impl(context, text):
   if text not in context.response:
       fail('%r not in %r' % (text, context.response))

success和failure會作為text參數傳入方法里面,然后會判斷是否在context.response里面启上。context.response會在given step里面被加入一點text

這樣的話 就可以多次使用同一個描述邢隧,實現step的重用

behave使用下面三種parsers

  • parse
  • cfparse
  • re

Context

在behave的step中都有一個context參數。這個參數的作用是用于存儲信息以及在不同的step中分享信息冈在。context在given when then三個level中都會run倒慧,并且有behave自動管理

當隨著測試執(zhí)行,behave執(zhí)行到了新的feature或是scenario包券,它給context加了a new layer纫谅,可以允許像增加新字段,或是overwrite以前定義好的溅固「讹酰可以看成這是一個scopes

context可以在step中間分享信息

@given('I request a new widget for an account via SOAP')
def step_impl(context):
    client = Client("http://127.0.0.1:8000/soap/")
    context.response = client.Allocate(customer_first='Firstname',
        customer_last='Lastname', colour='red')

@then('I should receive an OK SOAP response')
def step_impl(context):
    eq_(context.response['ok'], 1)

given里面起了一個服務器,然后發(fā)送了請求侍郭,response賦予了context.response询吴。在then中可以繼續(xù)使用context.response來檢查剛才的response里面有沒有我們需要驗證的值

除了context.response,還有context.text, context.table, context.failed可以使用

也可以自定義一些變量來傳遞

context.chrome = webdriver.Chrome()
context.server = simple_server.Server()

Environmental Controls

environment.py定義一些測試hook掠河,比如在event before 或是 after 執(zhí)行的方法

在feature level定義的environmental controls file會override scenario level;如果改變了scenario level是不會影響feature level

before_step(context, step), after_step(context, step)
These run before and after every step.
before_scenario(context, scenario), after_scenario(context, scenario)
These run before and after each scenario is run.
before_feature(context, feature), after_feature(context, feature)
These run before and after each feature file is exercised.
before_tag(context, tag), after_tag(context, tag)
These run before and after a section tagged with the given name. They are invoked for each tag encountered in the order they’re found in the feature file. Seecontrolling things with tags.
before_all(context), after_all(context)
These run before and after the whole shooting match.

The feature, scenario and step objects represent the information parsed from the feature file. They have a number of attributes:
上面方法的feature猛计, scenario唠摹, step對象代表著從feature file解析過來的信息。這些對象有著下面的屬性有滑。我們可以在測試中使用這些屬性信息

  • keyword: “Feature”, “Scenario”, “Given”, etc.
  • name: The name of the step (the text after the keyword.)
  • tags: A list of the tags attached to the section or step. See controlling things with tags.
  • filename and line: The file name (or “<string>”) and line number of the statement.

一個比較通用的用法是設置瀏覽器或是web server來運行你的測試

import threading
from wsgiref import simple_server
from selenium import webdriver
from my_application import model
from my_application import web_app

def before_all(context):
    context.server = simple_server.WSGIServer(('', 8000))
    context.server.set_app(web_app.main(environment='test'))
    context.thread = threading.Thread(target=context.server.serve_forever)
    context.thread.start()
    context.browser = webdriver.Chrome()

def after_all(context):
    context.server.shutdown()
    context.thread.join()
    context.browser.quit()

def before_feature(context, feature):
    model.init(environment='test')

Controlling Things With Tags

在behave中我們可以tag我們的feature文件。這就意味著我們能夠選擇的執(zhí)行feature或是feature文件中的scenarios

如果我們有下面這樣的feature 文件

Feature: Fight or flight
  In order to increase the ninja survival rate,
  As a ninja commander
  I want my ninjas to decide whether to take on an
  opponent based on their skill levels

  @slow
  Scenario: Weaker opponent
    Given the ninja has a third level black-belt
    When attacked by a samurai

    Then the ninja should engage the opponent
  @slow1
  Scenario: Stronger opponent
    Given the ninja has a third level black-belt
    When attacked by Chuck Norris
    Then the ninja should run for his life

如果我們執(zhí)行 behave —tags=slow嵌削,只有被tag為slow的scenario Weaker opponent被執(zhí)行

如果我們執(zhí)行 behave —tags=-slow, 只有被tag為slow的scenario Weaker opponent不被執(zhí)行

如果我們執(zhí)行behave —tags=slow,slow1 只要被tag為slow或slow1的scenario被執(zhí)行

如果我們執(zhí)行behave —tags=slow —tags=slow1 只要被tag為slow并且slow1的scenario被執(zhí)行

tags與environment.py的互動:

  • 如果一個feature或是scenario被skip了毛好,那么相應的before_和after_都不會執(zhí)行
  • environment.py中各方法中的feature/scenario對象都有tags屬性,這個屬性是列出了所有tag名稱的列表
  • environment.py中的before_tag和after_tag苛秕。如果這兩個方法被傳入了”slow”肌访,那么在執(zhí)行被tag為slow的scenario之前,這兩個方法會被調用

for example艇劫,部分scenario被tag為@browser吼驶,則我們可以使用feature.tags來查看tag有沒有browser.這樣做,我們可以指定哪些feature需要執(zhí)行這個before_和after_

def before_feature(context, feature):
    model.init(environment='test')
    if 'browser' in feature.tags:
        context.server = simple_server.WSGIServer(('', 8000))
        context.server.set_app(web_app.main(environment='test'))
        context.thread = threading.Thread(target=context.server.serve_forever)
        context.thread.start()
        context.browser = webdriver.Chrome()

def after_feature(context, feature):
    if 'browser' in feature.tags:
        context.server.shutdown()
        context.thread.join()
        context.browser.quit()

Debug-on-Error (in Case of Step Failures)

當一個step失敗的時候店煞,behave提供”debug on error/failure”功能來幫助debug蟹演。這個功能會通過”after_step()"這個hook來實現

當需要使用這個功能的時候,我們可以通過命令行傳入的配置數據來使其enable或是disable顷蟀。

用戶可以:

  • 在命令行提供相關參數(定義好的userdata)
  • 把參數值存儲在”behave.userdata”
# -- FILE: features/environment.py
# USE: behave -D BEHAVE_DEBUG_ON_ERROR         (to enable  debug-on-error)
# USE: behave -D BEHAVE_DEBUG_ON_ERROR=yes     (to enable  debug-on-error)
# USE: behave -D BEHAVE_DEBUG_ON_ERROR=no      (to disable debug-on-error)

BEHAVE_DEBUG_ON_ERROR = False

def setup_debug_on_error(userdata):
    global BEHAVE_DEBUG_ON_ERROR
    BEHAVE_DEBUG_ON_ERROR = userdata.getbool("BEHAVE_DEBUG_ON_ERROR")

def before_all(context):
    setup_debug_on_error(context.config.userdata)

def after_step(context, step):
    if BEHAVE_DEBUG_ON_ERROR and step.status == "failed":
        # -- ENTER DEBUGGER: Zoom in on failure location.
        # NOTE: Use IPython debugger, same for pdb (basic python debugger).
        import ipdb
        ipdb.post_mortem(step.exc_traceback)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末酒请,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子鸣个,更是在濱河造成了極大的恐慌羞反,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件囤萤,死亡現場離奇詭異昼窗,居然都是意外死亡,警方通過查閱死者的電腦和手機涛舍,發(fā)現死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門澄惊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人富雅,你說我怎么就攤上這事缤削。” “怎么了吹榴?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵亭敢,是天一觀的道長。 經常有香客問我图筹,道長帅刀,這世上最難降的妖魔是什么忙灼? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任藤韵,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘望蜡。我一直安慰自己,他們只是感情好躺苦,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布源葫。 她就那樣靜靜地躺著,像睡著了一般驱犹。 火紅的嫁衣襯著肌膚如雪嘲恍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天雄驹,我揣著相機與錄音佃牛,去河邊找鬼。 笑死医舆,一個胖子當著我的面吹牛俘侠,可吹牛的內容都是我干的。 我是一名探鬼主播蔬将,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼爷速,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了霞怀?” 一聲冷哼從身側響起遍希,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎里烦,沒想到半個月后凿蒜,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡胁黑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年废封,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丧蘸。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡漂洋,死狀恐怖,靈堂內的尸體忽然破棺而出力喷,到底是詐尸還是另有隱情刽漂,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布弟孟,位于F島的核電站贝咙,受9級特大地震影響,放射性物質發(fā)生泄漏拂募。R本人自食惡果不足惜庭猩,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一窟她、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蔼水,春花似錦震糖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至优炬,卻和暖如春颁井,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背穿剖。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工蚤蔓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卦溢,地道東北人糊余。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像单寂,于是被迫代替她去往敵國和親贬芥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容