## BDD 與 pytest-bdd
BDD 即 Behavior-driven development,行為驅動開發。BDD行為驅動是一種敏捷開發模式, 重點在于消除開發/測試對需求了解的歧義及用戶場景的驗證。
pytest-bdd 是一個BDD測試框架,類似于behave, cucumber。它可以統一單元測試和功能測試。
## 環境準備
首先需要確定是否安裝了pytest-bdd框架,通過 `pip show pytest-bdd` 命令可以查看是否安裝了pytest-bdd框架:

出現如上頁面說明沒有安裝,安裝pytest-bdd框架:使用pip安裝pytest-bdd模塊。
```
pip install pytest-bdd
```
備注: 也可以使用 `pip list` 命令查看所有已經安裝的模塊。
##示例場景與步驟
這里測試一個加法運算器, 在實際場景中驗證加法使用Unit 測試即可,BDD 一般用作功能測試,這里為了演示方便使用該場景作為示例。
具體步驟如下:
1. 創建用戶場景文件(.feature后綴的文件)
2. 編寫步驟函數和測試場景。(這兩者可以分開為不同文件,也可以合并在一起寫)
3. 開始測試
## 項目目錄
pytest-bdd 對于目錄的要求非常靈活,可以根據自己的項目結構進行配置。通常情況下,pytest-bdd 會將 feature 文件和 step 實現文件分別放置在不同的目錄中,這兩個目錄可以分別為`features/`和`step_defs/`,也可以根據自己的需要進行修改。同時,pytest-bdd 還可以支持多個 feature 目錄和 step 實現目錄,只需要在配置文件中進行相應的配置即可。
除了 feature 文件和 step 實現文件的目錄外,pytest-bdd 還可以支持其他的目錄,例如 fixture 目錄、data 目錄等。這些目錄同樣可以根據自己的需要進行配置。總之,pytest-bdd 的目錄要求非常靈活,可以根據自己的項目進行配置。
簡單起見, 這里演示的目錄結構如下所示:
```
├────features/ # 用戶場景
│ ├────calculator.feature
├────step_defs/ # 步驟函數和測試場景
│ ├────test_calculator.py
├────util/
│ └────calculator.py # 需要測試的計算器類
```
## 文件內容
0. 用于測試的計算器類文件calculator.py的內容如下:
```
# calculator.py
class Calculator:
def add(self, a, b):
return a + b
def subtract(self, a, b):
return a - b
def multiply(self, a, b):
return a * b
def divide(self, a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
```
1. 用戶場景文件calculator.feature 內容如下:
```
Feature: Addition
Scenario: Add two numbers
Given I have a calculator
When I enter "1" and "2"
Then the result should be "3"
```
2. 步驟函數和測試場景文件test_calculator.py 內容如下:
```
import sys
import os
import pytest
#sys.path.append('D:/devworkspace/python-ency/chp3/tests/bdd/util')
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'util'))
from calculator import Calculator
from pytest_bdd import scenario, given, when, then, parsers
@scenario('../features/calculator.feature','Add two numbers')
def test_add():
print(sys.path.append(os.path.dirname(os.path.dirname(__file__))+'util'))
pass
@pytest.fixture
@given("I have a calculator")
def calculator():
return Calculator()
@when(parsers.parse('I enter "{a}" and "{b}"'))
def enter_numbers(calculator, a, b):
calculator.a = int(a)
calculator.b = int(b)
@then(parsers.parse('the result should be "{result}"'))
def verify_result(calculator, result):
assert calculator.add(calculator.a, calculator.b) == int(result)
```
## 測試
命令行切換到對應的目錄,執行 `pytest`即可。執行的效果如下圖:

## 問題
### 自定義類的導入方式
```
from calculator import Calculator
E ModuleNotFoundError: No module named 'calculator'
```
1. 添加絕對路徑導入
```
import sys
sys.path.append("E:/mybdd/util")
```
2. 添加上層目錄
```
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
```
修改`sys.path`之后的導入行為會對你的整個 Python 環境產生影響,使用時需要謹慎。
3. 相對導入
`.`來表示當前目錄,兩個(或更多的)點`..`來表示上一層(或更多層)目錄
相對導入只有在作為模塊的一部分時才能工作,也就是說,你不能直接運行一個使用了相對導入的 Python 文件,你需要通過主模塊或者 -m 標志運行。
Python 的相對導入基于當前的模塊名稱。所以,你不能在一個腳本直接運行時使用相對導入,因為腳本的`__name__`屬性為`__main__`,Python 就不知道如何找到父模塊或者兄弟模塊。
**將你的腳本作為模塊執行**
你可以使用`-m`選項來告訴 Python 運行包含相對導入的腳本作為一個模塊。首先,確保你在當前執行目錄的上級目錄中,然后使用類似以下的命令:
```
python -m mypackage.mysubpackage.mymodule
```
對于相對導入,你必須以包的方式運行你的項目(也就是說目錄下需要有`__init__.py`文件,讓Python把這個目錄看作包),例如你可能需要在項目的頂層目錄下運行`python -m main`,而非`python main.py`。
4. **使用環境變量 PYTHONPATH**:
也可以通過在環境變量`PYTHONPATH`中添加你的模塊所在的目錄,Python 在運行時會添加這些目錄到`sys.path`中,這樣就可以搜索到你的模塊了。
export PYTHONPATH="${PYTHONPATH}:/my/new/path"
### fixture 'calculator' not found
```
@pytest.mark.usefixtures(*func_args)
def scenario_wrapper(request: FixtureRequest, _pytest_bdd_example: dict[str, str]) -> Any:
E fixture 'calculator' not found
```
注意下面代碼
```
@pytest.fixture
@given("I have a calculator")
def calculator():
return Calculator()
```
`@pytest.fixture` 這一句必須加上, 很多在線的例子中都沒有
### ERROR test\_calculator.py - TypeError: 'NoneType' object is not callable
```
@scenario('../features/calculator.feature','Add two numbers')
#@scenarios('../features/calculator.feature')
def test_add():
pass
```
注意:使用scenarios會出現錯誤
## 本篇完整示例
* [https://download.csdn.net/download/oscar999/88532317](https://download.csdn.net/download/oscar999/88532317)
## 參考
* [https://github.com/pytest-dev/pytest-bdd](https://github.com/pytest-dev/pytest-bdd)
* [https://pytest-bdd.readthedocs.io/en/stable/](https://pytest-bdd.readthedocs.io/en/stable/)
*****
*****
- 前言
- 1.入門篇
- Python介紹
- 安裝與使用
- Python開發利器之VS Code
- 模塊安裝
- 命令行
- 一次Python無法安裝模塊的問題探索與解決之旅
- 命令運行
- Conda
- 下載地址
- 2.基礎篇
- 基礎語法
- 輸入與輸出
- with as的用法
- 注釋
- Python命令行參數
- 編碼
- 變量類型
- 列表遍歷
- 運算符
- 表達式語句
- 條件
- 循環
- 日期和時間
- 函數
- 高級語法
- @符號-裝飾器
- 模塊和包
- name
- init.py
- 錯誤和異常
- 面向對象
- 3.專題篇
- 常用功能
- Python 字符串連接
- python web
- Python CGI編程
- Python OAuth2
- 認證 Flask-HTTPAuth
- 常用命令
- 內置函數
- dir()
- print(f)
- 標準模塊
- sys
- pickle-數據序列化
- os
- IO(輸入輸出)
- 鍵盤輸入
- 文件讀寫
- 測試
- Python測試框架之pytest快速入門
- pytest-bdd快速示例和問題解決
- 基于pytest-bdd的項目目錄結構和命名規范
- python BDD 的相關概念
- Behave介紹和快速示例
- Python BDD之Behave測試報告
- Python BDD 框架比較之 pytest-bdd vs behave
- pytest進階
- Flask + pytest測試
- 參考網址
- pytest-bdd進階
- hehave進階
- 測試路徑
- python + selunium
- HTML 根據多層CSS 查找元素
- 等待執行
- 使用text 查找 span
- pytest如何在控制臺輸出
- 4.問題篇
- pip pip3 及區別
- TypeError: can only concatenate str (not "NoneType") to str
- 5.實戰篇
- matplotlib-繪圖包
- 導入類
- 命名規范
- 模塊查找
- 6.進階篇
- Flask
- Flask介紹
- Flask擴展模塊
- Flask-Login
- 問題
- Jinja2
- Flask-RESTful
- Flask-JWT-Extended
- WSGI
- Flask-SQLAlchemy
- 部署
- Flask VS Django
- Flask Web
- Flask + Vue
- Flask實戰
- Flask 標準目錄結構
- Blueprints
- 參考
- FastAPI 測試
- https 證書 Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate