python BDD 框架之lettuce

蟲師發表於2013-11-09

 

http://pythonhosted.org/lettuce/

 

介紹                                                                                                     

 

  親們!看到TDD/BDD 一定會感覺高階大氣上檔次,不是我們普通弔民玩的,最的近在一直在摸索自動化測試。也想體驗並引入BDD 低調奢華的內涵。於是,在網路上搜尋資料;話說這玩藝兒真的不太好理解,尤其對於沒有豐富程式設計的經驗的同學。

  學習BDD ruby cucumber  是個不錯的選擇,但我是python 流的,自然找了來它的兄弟lettuce ,從官方版本(v0.1rc11)來看確實夠年輕的,不過由ruby cucumber 在前面開路,lettuce 應該會發展的很順利。

  lettuce 除了官方文件外,幾乎找不到其它資料,為了理解lettuce  ,我們不妨多去看看cucumber 的資料。

 

  lettuce是一個非常有用的和迷人的BDD(行為驅動開發)工具。Python專案的自動化測試,它可以執行純文字的功能描述,就像ruby語言的cucumber

  lettuce,使開發和測試過程變得很容易,可擴充套件性,可讀性和-什麼是最好的-它允許我們用自然語言去描述個一個系統的行為,你不能想象這些描述可以自動測試你的系統。

 

 

 

 

安裝                                                                                                        

 

請確認你已經安裝了python 以及pip安裝包管理工具。

不管是windows 還是linux 環境,進入pip目錄,只需下面一個命令就可以安裝lettuce .

user@machine:~$ [sudo] pip install lettuce

 

 

例子(階乘)                                                                                           

 

下面就通過官網的例子來領略lettuce的風騷。

什麼階乘?

0=1

1=1

2=2×1=2

3=3×2×1=6

....

10!=10×9×8×7×6×5×4×3×2×1=3628800

.....

下面是用python 語言的兩種階乘實現:

#coding=utf-8

#迴圈實現
def f(n):
    c = 1
    for i in range(n):
        i=i+1
        c=c*i
    return c

#遞迴實現
def f2(n):
    if n > 1:
        return n*f2(n-1)
    else:
        return 1

#呼叫方法    
print f(10)
print f2(10)

OK!介於我們理解上面階乘的基礎上,來看看BDD是如何實現的。

 

 

lettuce 解析                                                                                         

 

建立以下目錄結構:

.../tests/features/zero.feature

                         /steps.py

 

現在我們來編寫zero.feature 檔案的內容 

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

 基於E文不好的同學,我這裡簡單翻譯一下:

功能:計算階乘
   為了使用lettuce
   作為初學者
   我們將實現階乘
   
   場景:0的階乘
      假定我有數字0
      當我計算它的階乘
      然後,我看到了1
   

    是不是很自然的描述?!第一段功能介紹,我要實現什麼功能;第二段場景,也可以看做是一條測試用例,當我輸入什麼資料時,程式應該返回給我什麼資料。

     雖然是自然語言,但還是有語法規則的,不然一千個人有一千中描述,程式以什麼樣的規則去匹配呢?

      其實它的語法規則非常簡單就幾個關鍵字,記住他們的含義及作用即可。

----------------------------------------------

  • Feature(功能)
  • Scenario(情景)
  • Given(給定)
  • And(和)
  • When(當)
  • Then(則)

----------------------------------------------

他們的含義與原有自動化測試框架的概念相似,類比如下

關於feature檔案的作用,執行以及語法規則將在下一節中詳細介紹,這一節主要先來體驗luttuce的風騷。

 

有了上面zero.feature檔案的行為做指導,下面開啟steps.py 檔案來編寫我們的程式。

from lettuce import *

@step('I have the number (\d+)')
def have_the_number(step, number):
    world.number = int(number)

@step('I compute its factorial')
def compute_its_fatorial(step):
    world.number = factorial(world.number)

@step('I see the number (\d+)')
def check_number(step, expected):
    expected = int(expected)
    assert world.number == expected, \
        "Got %d" % world.number

def factorial(number):
    number = int(number)
    if (number == 0) or (number == 1):
        return 1
    else:
        return number

   我擦!咋一看怎麼跟我上面實現階乘的程式碼相差甚遠呀!不知道你和你的小夥伴有沒有驚呆!?好吧,以我拙劣的python語言水平試著來分析一下,這是啥?這是啥?這又是啥?

 

from lettuce import *

引入lettuce 下面的所有包

 

@step('I have the number (\d+)')

@step 字面意思是步驟

I have the number (\d+) 對應的就是zero.feature檔案中的第六句:Given I have the number 0 

(\d+) 是一個正規表示式,\d 表示匹配一個數字,表示匹配的數字至少有一個或多個。關於這個可以參考其他python 正規表示式的資料。

 

第一步:

@step('I have the number (\d+)')
def have_the_number(step, number):
    world.number = int(number)

定義一個方法have_the_number,把假設的輸入0)轉換成整型放入world.number中。

 

第二步:

@step('I compute its factorial')
def compute_its_fatorial(step):
    world.number = factorial(world.number)

把have_the_number方法中world.number的變數值(0)放入 factorial() 方法中,並把結果返再賦值給world.number變數。

I compute its factorial  對應的就是zero.feature檔案中的第七句:When I compute its factorial

 

第三步:

def factorial(number):
    number = int(number)
    if (number == 0) or (number == 1):
        return 1
    else:
       return number

 這個是factorial()方法被呼叫時的處理過程,對引數的內容轉換成整數,判斷如果等於01的話就直接返回1,否則返回具體的數。(處理結果給了第三步的world.number)

 

第四步:

@step('I see the number (\d+)')
def check_number(step, expected):
    expected = int(expected)
    assert world.number == expected, \
        "Got %d" % world.number

expected 獲取的是zero.feature檔案中預期結果, 與第三步處理的實際結果(world.number)進行比較;assert 函式進行斷言結果是否正確。

I see the number (\d+)對應的就是zero.feature檔案中的第八句:Then I see the number 1

 

 

執行                                                                                                        

 

切換到tests目錄下,執行lettuce 命令:

fnngj@fnngj-H24X:~/python/lettuce/tests$ lettuce

執行結果如下:

執行結果很清晰,首先是zero.feature檔案裡功能描述(feature),然後場景(scenario)每一步所對應steps.py 檔案裡的哪一行程式碼。

最後給出執行結果:

Feature(1 passed)   一個功能通過

Scenario(1 passed)   一個場景通過

Steps(3 passed)      三個步驟通過

 

 

完善                                                                                                         

 

下面我們可以在zero.feature 中多加幾個場景(測試用例):

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

 

執行結果:

 

  嗯??第四場景沒通過,3=3*2*1=6 這個預期結果肯定是正確的,那就是程式碼的邏輯有問題吧!如果你細心的話一定發現了setup.py中的程式碼並未真正實現階乘,我們需要對它進行修改:

#coding=utf-8
from lettuce import *

@step('I have the number (\d+)')
def have_the_number(step, number):
    world.number = int(number)
    print world.number

@step('I compute its factorial')
def compute_its_fatorial(step):
    world.number = factorial(world.number)
    print world.number

@step('I see the number (\d+)')
def check_number(step, expected):
    expected = int(expected)
    assert world.number == expected, \
        "Got %d" % world.number

def factorial(number):
    number = int(number)
    if (number == 0) or (number == 1):
        return 1
    else:
        return number*factorial(number-1)

程式碼修改部分:

def factorial(number):
    number = int(number)
    if (number == 0) or (number == 1):
        return 1
    else:
        return number*factorial(number-1)

參照本文開頭,通過遞迴的方式實現階乘的程式碼,現在才算完整的實現階乘。OK !再來執行以下指令碼吧!全綠了有木有!!

 

這應該是目前講解BDD 最通俗易懂的文章了,沒有之一,給個贊吧!

 

相關文章