在Python 語言中進行BDD的規格和測試文件的編寫的時候,常常會遇到下面的概念:
- Fixture : 測試設施。設定測試環境的預設狀態或值的機制。
- Background: 背景。所有場景的公共部分。
- Scenario: 場景。
- Given : 前置條件
- When: 用戶操作
- Then:預期結果
規格文件
pytest-bdd 和Behave 是Python中比較流行的BDD 框架,兩種使用的規格文件基本類型,規格文件都是以 .feature結尾,一個需求文件中只能有一個Feature字段,可以包含多個Scenario(用戶場景)。
Given->When->Then類似于準備->執行->驗證/清理的流程。
- Given:一般可以用來做預置條件/數據準備,下面第一個And也屬于Given。
- When下面的And都屬于When, 一般是操作步驟。
- Then: 一般用于驗證結果(斷言),也可以進行清理數據。
Fixture 測試設施
在軟件測試中,“Fixture” 是一種設定測試環境的預設狀態或值的機制。通常為了某個測試或一組測試,需要一些預設的對象、文件或數據庫等等。如何設置和管理這些預設狀態就是 “Fixture” 的作用,它通常用來初始化測試環境,或者在每次測試完成后進行清理。
考慮到這個功能,有些中文翻譯可能會把 “Fixture” 翻譯為 “固件” 或 **“前置條件” **或 “測試設施”。然而,這通常還需要結合上下文具體理解。在很多開發文檔或技術討論中,也常常直接使用英文 “Fixture”。
在 BDD 和測試中,Fixture 是設置給定環境或編寫 “Given” 步驟所需的一項工作。可以把它看作是一個重復使用的預設條件,使你能夠用一致的環境運行測試。這些預設條件可能涉及各種各樣的事情,比如創建數據庫、初始化變量、創建類的實例,或者運行特定的命令等。
在 pytest,這些 fixtures 是用 @pytest.fixture 裝飾器聲明的 Python 函數。例如:
@pytest.fixture
def client():from myapp import MyClientreturn MyClient()
這個 client fixture 可以在測試函數、類或模塊中通過參數 re-use。
def test_get_data(client):data = client.get_data()assert data is not None
在 pytest-bdd 中,fixture 的使用方式非常相似,但支持為步驟 (given, when, then) 設置 fixture,使在 BDD 測試中更容易設置和共享上下文。
舉個例子:
from pytest_bdd import given, scenario, then, when
import pytest@scenario('calculations.feature', 'Adding numbers')
def test_add():pass@pytest.fixture
def calc():return Calculator()@given('I have a calculator', target_fixture='calculator')
def i_have_a_calculator(calc):return calc@when('I add <number1> and <number2>')
def add_numbers(calculator, number1, number2):calculator.add(int(number1), int(number2))@then('I should get <result>')
def get_result(calculator, result):assert calculator.result == int(result)
在這個例子中,
- @pytest.fixture 注解的 calc()方法創建了一個 Calculator 類的實例
@given('I have a calculator', target_fixture='calculator')
的作用是 返回一個名為“calculator”的fixture(即測試用例執行前需要準備的對象或數據)。這個fixture可以在之后的測試步驟中被引用和使用。
使用這種方式的好處是在 BDD 的步驟中更容易地重用和共享設置和數據。同時,它也可以幫助你分離你的測試代碼,使得它們更加模塊化和易維護。
Background 是什么?
在 BDD 中,Background
關鍵字用于定義在執行每個場景時都需要執行的步驟。通常,這些步驟用于設置初始條件或預設環境。換句話說,它是需要在每個場景開始之前運行的給定(Given
)步驟。
以下是一個例子,演示如何在 Background 中設置初始的環境狀態:
Feature: 乘坐地鐵Background:Given 我有一張有效的地鐵卡Scenario: 乘坐地鐵When 我刷地鐵卡Then 旋轉門應該打開Scenario: 余額不足Given 我的卡余額是0When 我刷地鐵卡Then 旋轉門不會打開
在這個示例中,Background
包含了一個 “Given 我有一張有效的地鐵卡” 的步驟。這意味著在執行每個場景 “乘坐地鐵” 和 “余額不足” 之前,測試始終會先執行這個步驟。
Background
的目的是幫助減少測試場景之間的重復,并傳達對所有場景都有效的前提條件。但需要注意的是,過度使用Background
可能會使測試復雜化和難以理解,特別是在Background
中有很多步驟或它們的效果不明顯時。
Fixture 和 Background 的區別
Fixture
和Background
都是在開啟測試前設置預期環境或狀態的工具,但它們在用途和功能上有所區別,用在不同的場景。
-
Fixture:在 pytest 和許多其他測試框架中,fixture 是設置測試環境的一種工具,它為測試提供了需要的初識狀態或值。可以把 pytest fixture 理解為設置給定環境或預設需要的工作,它可以創建對象、連接數據庫、開啟服務器或其他任何為了測試需要預先設定的操作。Fixture 可以被多個測試用例重復使用,從而避免代碼的重復。
-
Background:在 Gherkin 語言(被許多 BDD 框架,如 Cucumber 和 behave 使用)中,Background 是一個特殊的場景,它在包含它的
Feature
中的每個Scenario
或Scenario Outline
開始前運行。我們可以把 Background 理解成作為每個場景前提的共享步驟。背景的目標是為了消除場景之間的冗余。
它們的區別主要在于:
- Background 是 BDD 中 Gherkin 語法的一部分,用于描述在每個測試場景開始時的共享行為,
- 而 Fixture 是 pytest 測試中的一個概念,它更傾向于代碼層面,負責進行一些設置和清理工作。
Background 和Fixture 也可以一起使用。例如,在 Given 步驟中使用fixture
去實現在Background
中描述的行為。
Feature: 乘坐地鐵Background:Given 我有一張有效的地鐵卡
對應的Given
實現可能如下:
@given("我有一張有效的地鐵卡", target_fixture="card")
def have_subway_card():return SubwayCard(is_valid=True)
在這個例子中,have_subway_card
就是一個 fixture,它在Background
描述的Given
步驟中被使用。
Scenario 用戶場景
在行為驅動開發(Behavior-Driven Development,BDD)中,Scenario
是描述一個特定功能如何在特定情境下工作的方法,或者說是描述功能如何被使用的故事。每個Scenario
是一個完整的可測試的用戶故事。
Scenario
通常使用 “Given-When-Then” 的格式描述,這種格式可以清晰地闡述環境條件、行為和期望結果。每個Scenario
應該是自足并可以單獨運行。
以下是一個例子:
Scenario: User login with correct username and passwordGiven a user has been registered with username "user1" and password "pass1"When the user login with username "user1" and password "pass1"Then the login should be successful
在這個例子中:
-
Given
步驟設定了開始條件,即已經有一個注冊用戶 “user1”,其密碼是 “pass1”。 -
When
步驟描述了用戶嘗試使用正確的用戶名和密碼登錄的行為。 -
Then
步驟描述了期望的結果,即登錄應該成功。 -
每個
Scenario
開始都是獨立的,不依賴于其他Scenario
,每個Scenario
均應清理其測試環境,以確保不會影響其他測試。這就是所謂的測試原子性。 -
Scenario
是 BDD 的核心部分,它幫助我們將抽象的需求轉化為具體、可執行和可驗證的測試。