pytest-bdd 初體驗

pytest-bdd 是用 python 實作 cucumber 的框架,有在寫測試的開發者,肯定不能錯過

開發環境

安裝環境

建立虛擬環境

uv venv

 

還原依賴套件

uv pip install -e .

 

或是安裝

uv add "pytest-bdd>=4.46.0"

 

pytest-bdd

根據 Gherkin/Cucumber 語言描述測試案例

feature

#features/calculator.feature

測試步驟如下

Feature: 計算機基本運算
  作為一個使用者
  我想要使用計算機進行基本運算
  以便能夠快速得到計算結果

  Scenario Outline: 基本四則運算
    Given 我有一個計算機
    When 我輸入第一個數字 <num1>
    And 我選擇運算符號 <operator>
    And 我輸入第二個數字 <num2>
    And 我按下 = 鍵
    Then 我應該得到結果 <result>

    Examples:
      | num1 | num2 | operator | result |
      | 5    | 3    | +        | 8      |
      | 10   | 4    | -        | 6      |
      | 6    | 7    | *        | 42     |
      | 20   | 5    | /        | 4      |

....

 

alt + enter pycharm 開發工具會出現建立步驟的選單(create step definition),可以選擇新增步驟或是選擇已經存在的檔案

 

step

透過 IDE 產生一個空的 step

#test_calculator_step.py

@given("我有一個計算機")
def step_impl():
    raise NotImplementedError(u'STEP: Given 我有一個計算機')

 

target_fixture

target_fixture 是一個裝飾器,用途:

  • 用於 given step (@given 裝飾器 )
  • 可被後續的 steps(when, then)使用,也就是 scenario 中共享狀態
  • context 是字典,會透過它傳遞狀態
@given('我有一個計算機', target_fixture='context')
def 我有一個計算機(calculator):
    return {'calculator': calculator}

 

第一個 step 還需要初始化被測目標物 

@pytest.fixture
def calculator():
    return Calculator()

 

連結 .feature 和 step

scenarios('./features/calculator.feature')

 

parsers.parse

  • 用於從 Gherkin 語句中提取參數,支援多種格式化字串的解析,匹配 feature 檔案中的步驟
  • {num1:d}
    • num1 是參數名稱
    • :d 表示這是一個整數(decimal)參數
@when(parsers.parse("我輸入第一個數字 {num1:d}"))
def 我輸入第一個數字(context, num1):
    context['num1'] = num1

context['num1'] = num1,將解析後 num1 放進 context 字典裡

 

接下來的 step,就是把資料放進 context,再拿出來運算

@when(parsers.parse("我輸入第一個數字 {num1:d}"))
def 我輸入第一個數字(context, num1):
    context['num1'] = num1


@when(parsers.parse("我輸入第二個數字 {num2:d}"))
def 我輸入第二個數字(context, num2):
    context['num2'] = num2


@when(parsers.parse("我選擇運算符號 {operator}"))
def 我選擇運算符號(context, operator):
    context['operator'] = operator

 

運算

@when("我按下 = 鍵")
def 我按下鍵(context):
    num1 = context['num1']
    num2 = context['num2']
    operator = context['operator']
    calculator = context['calculator']

    # 定義運算符對應的計算機方法
    operatorMap = {
        '+': calculator.add,
        '-': calculator.subtract,
        '*': calculator.multiply,
        '/': calculator.divide
    }

    if operator not in operatorMap:
        raise ValueError(f"不支援的運算符號: {operator}")

    try:
        # 直接從字典取得對應的方法並執行
        result = operatorMap[operator](num1, num2)
        context['result'] = result
    except ValueError as e:
        context['error'] = str(e)

 

最後的 step 就是 assert,驗證期望跟實際運算結果

@then(parsers.parse("我應該得到結果 {result:d}"))
def 我應該得到結果(context, result):
    assert context['result'] == result


@then(parsers.parse('我應該看到錯誤訊息 "{error_message}"'))
def 我應該看到錯誤訊息(context, error_message):
    assert context['error'] == error_message

 

執行測試

執行所有測試

pytest -v

 

執行某一個測試

pytest -v -k "test_兩個數字相減"

 

範例位置

https://github.com/yaochangyu/sample.dotblog/tree/3873afc358085ca97a9a0b61d8ed79bdef25f7ce/Test/Lab.Py.BDD

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo