Python Note 03
介紹pytest的fixture、conftest功能,透過pytest的一些插件來產生報表。

基於Pytest框架的自動化測試 (下)
前面介紹了直接使用測試函數、用class包裝測試、setup
和teardown
、參數化測試案例、驗證預期的錯誤和跳過錯誤,這些是pytest基本的測試方法,接下來會透過pytest的fixture、conftest來整合這些功能,還有透過pytest的一些插件來產生報表,讓pytest的功能變得更加強大和靈活。
進階功能
接下來介紹的是 pytest 的進階功能,分別有fixture
、conftest
和產生報表的方法。
fixture
fixtures是pytest中的一個關鍵概念,他有前面介紹的setup
跟teardown
一樣的功能,可以幫助建構測試所需的環境,並在測試函數中重複使用:
import pytest
@pytest.fixture
def setup_teardown_example():
# 在測試之前執行的代碼
test_data = "在資料庫中準備測試資料"
yield test_data
# 在測試之後執行的代碼
print("清理測試資料")
def test_using_fixture(setup_teardown_example):
test_data = setup_teardown_example
# 使用 fixture
assert something == expected_result
fixture可使用的參數有下列三種:
scope
:表示作用域,預設為function
。name
:用來設定fixture的別名,預設為函式名稱autouse
:預設為False,若為True,則會自動進行使用 (根據scope作用域而定)
其中fixture的scope
作用域跟setup
跟teardown
有略微不同,總共有四種,分別為:
function
: 預設的選項,在每個測試函數運行之前和之後執行,各測試函數都將獲得一個新的、獨立的fixture實例,不會共享數據。class
: Fixture將在每個測試類 (使用@pytest.mark.usefixtures
裝飾器) 中的測試函數之前和之後執行,在多個測試函數之間共享相同的fixture數據非常有用。module
: 在整個測試模塊 (測試文件) 中的測試函數之間執行,對於在模塊級別共享數據非常有用。session
: 整個pytest會話 (所有測試模塊) 中的測試函數之前和之後執行,允許在多個模塊之間共享持久性數據。
比較特別的是fixture在class上的應用,以下是使用@pytest.mark.usefixtures
的範例,fixture會在測試class的所有方法之前運行一次,會在整個class中共享數據,然後在所有測試方法執行完成後進行清理:
import pytest
@pytest.fixture(scope="class")
def class_fixture():
# 在測試類別中的測試方法之前和之後執行一次的fixture
print("Class fixture setup")
yield
print("Class fixture teardown")
@pytest.mark.usefixtures("class_fixture")
class TestClassWithFixture:
def test_method1(self):
print("Test method 1")
def test_method2(self):
print("Test method 2")
以下展示了fixture在各個層級使用的範例,在這個範例中展示了fixture在session創建了與資料庫的連結;在module層級上的指定了測試用的collection並插入數據用於測試;在class層級上新增了測試資料讓後續的class級別的測試方法可以進行測試:
import pytest
from pymongo import MongoClient
# 定義一個具有session級別MongoDB client的fixture
@pytest.fixture(scope="session")
def mongodb_connection():
# 連接到MongoDB的Client
client = MongoClient('localhost', 27017)
# 使用測試用的資料庫
db = client['mytestdb']
yield db
# 清理測試用資料庫
client.drop_database('mytestdb')
# 定義一個具有module級別的資料庫連接fixture,用於插入Document
@pytest.fixture(scope="module")
def setup_mongodb_data(mongodb_connection):
# 使用測試集合名稱寫入數據
collection = mongodb_connection['mycollection']
data = [
{"_id": 1, "name": "user1"},
{"_id": 2, "name": "user2"},
]
collection.insert_many(data)
yield
collection.delete_many({})
# 定義一個class級別的fixture
@pytest.fixture(scope="class")
def class_fixture(mongodb_connection, setup_mongodb_data):
# 在測試的class中所有測試方法之前和之後運行一次的fixture設置
collection = mongodb_connection['mycollection']
data = {"_id": 3, "name": "user3"}
collection.insert_one(data)
yield
collection.delete_one({"_id": 3})
# 在測試的class中使用前面設定的fixture進行測試
@pytest.mark.usefixtures("class_fixture")
class TestMongoDBClass:
def test_fetch_user_data(self, mongodb_connection):
collection = mongodb_connection['mycollection']
user = collection.find_one({"_id": 1})
assert user["name"] == 'user1'
def test_another_test(self, mongodb_connection):
collection = mongodb_connection['mycollection']
user = collection.find_one({"_id": 2})
assert user["name"] == 'user2'
def test_class_fixture(self, mongodb_connection):
collection = mongodb_connection['mycollection']
user = collection.find_one({"_id": 3})
assert user["name"] == 'user3'
conftest
conftest.py
是pytest中一個特殊的模塊,它允許在不同的測試文件之間共享 fixture、自定義 hook 函數和配置選項,簡而言之就是一個可以存放經常被使用到 fixture的地方。
pytest在一開始執行時,就會先去抓是否有conftest.py
的存在,而存放在conftest.py
當中的fixture不需要透過 import就可以直接進行使用,如果有某些fixture會被許多個test module使用的話,就可以試著將該fixture放到conftest.py
當中。
conftest.py
的名稱和位置是固定的,必須放置在測試項目的根目錄或子目錄下,pytest會自動識別並加載它,conftest.py
所存在的當前目錄以及其所有子目路中的test case都可以使用,若於不同目錄則需要另外寫一個conftest.py
。
使用conftest.py
的的資料夾結構可以如下:
├─src
│ ├─pkg1
│ │ ├─__init__.py
│ │ └─module_a.py
│ └─pkg2
│ ├─__init__.py
│ ├─module_a.py
│ └─module_b.py
├─tests
│ ├─test_pkg1
│ │ ├─__init__.py
│ │ └─test_case.py
│ ├─test_pkg2
│ │ ├─__init__.py
│ │ ├─test_case.py
│ │ └─conftest.py
│ ├─__init__.py
│ ├─conftest.py
│ └─test_1.py
└─setup.py
產生報表
前面介紹了許多使用pytest的方法,當使用pytest執行測試時,產生報表也是非常重要的,這是因為測試報表具有以下幾個關鍵用途和優勢:
- 可視化測試結果: 測試報表可以將測試結果以易於理解的方式呈現,通常用顏色和標籤來區分通過的測試、失敗的測試和錯誤的測試,使開發人員能夠快速識別和定位問題,並更容易理解測試的運行情況。
- 歷史紀錄: 測試報表通常可以保存歷史測試運行的結果,開發團隊可以追蹤測試運行的變化,對於檢測測試覆蓋率的提高或測試性能的改進非常有用。
- 集成到CI/CD流程: 在CI/CD流程中,測試報表可以自動生成並保存,這樣團隊可以輕鬆地監視應用程序的測試狀態,並確保每次部屬都是穩定的。如果測試報告顯示問題,則可以停止部屬,以防止潛在的錯誤進入生產環境。
- 分析測試結果: 測試報表還可以提供測試運行時間、測試覆蓋率、測試失敗的統計訊息等等,這有助於開發人員進行性能分析和測試覆蓋率分析。
以下是pytest常用產生報表的插件:
pytest-json-report
: 用於生成JSON格式的測試報告。
使用方法:pytest --json-report=path/to/report.json
pytest-cov
: 用於測試覆蓋率報告,可生成HTML供檢視。
使用方法:pytest --cov=<your_package> --cov-report=html
pytest-html
: 生成漂亮的HTML測試報告 (4.0.2會有問題,目前使用3.2.0)。
使用方法:pytest --html=report.html --self-contained-html
allure-pytest
: Allure是一個開源的測試報告框架,可用於紀錄、展示和分析測試結果,下面單獨介紹如何使用這個套件。
Allure
安裝Allure需要Java JDK支持,需要到這個網站安裝,接下來直接到Allure的Github網址下載最新版本Allure的zip檔,將allure-x.x.x/bin放到系統的PATH中,可以在命令列上輸入allure --version
檢查是否安裝成功。
接著要安裝allure在pytest的套件:
poetry add allure-pytest
可以直接在專案內執行以下命令,可以得到測試結果的資料:
pytest --alluredir ./allure/data
可以透過以下命令產生出html檔案,可以將index.html放到瀏覽器中查看報告:
allure generate ./allure/data -o ./allure/report/ --clean
不過用這種方式可能會遇到網頁打開了,但讀取不到數據的問題,這是因為CORS policy error的問題,如果是使用Google Chrome,在啟動時需要加上以下的參數以避免CORS policy error:
--allow-file-access-from-files
或是可以透過以下命令直接建構一個本地Web服務器:
allure serve ./allure/data
Allure還有許多其他的功能,具體可以參考官方文檔,後續有機會也可以專門為Allure寫一篇介紹文。
結論
測試框架在軟件開發過程中扮演著重要的角色,它不僅幫助測試人員高效地執行測試,還有助於開發團隊確保產品質量和穩定性,測試框架是保證軟件品質的不可或缺的工具之一。除錯和測試是軟體開發過程中的正常部分,不要害怕花時間來進行這些工作,每次修復一個錯誤,程式將變得更加穩定和可靠,持續學習和改進測試和除錯技巧也是提高軟體開發效率的關鍵。