良好的整合實務

使用 pip 安裝套件

對於開發,我們建議您使用 venv 進行虛擬環境設定,並使用 pip 安裝您的應用程式和任何依賴項,以及 pytest 套件本身。這確保您的程式碼和依賴項與您的系統 Python 安裝隔離。

在您的儲存庫根目錄中建立一個 pyproject.toml 檔案,如 Packaging Python Projects 中所述。前幾行應如下所示

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "PACKAGENAME"
version = "PACKAGEVERSION"

其中 PACKAGENAMEPACKAGEVERSION 分別是您的套件的名稱和版本。

然後,您可以從同一個目錄執行以下命令,以「可編輯」模式安裝您的套件

pip install -e .

這讓您可以隨意更改您的原始程式碼(包括測試和應用程式)並重新運行測試。

Python 測試探索的慣例

pytest 實作了以下標準測試探索

  • 如果未指定任何參數,則從 testpaths(如果已配置)或當前目錄開始收集。或者,可以使用目錄、檔案名稱或節點 ID 的任意組合的命令行參數。

  • 遞迴進入目錄,除非它們與 norecursedirs 相符。

  • 在這些目錄中,搜尋 test_*.py*_test.py 檔案,並通過它們的 測試套件名稱 導入。

  • 從這些檔案中,收集測試項目

    • 類別外部以 test 為前綴的測試函數或方法。

    • 在以 Test 為前綴的測試類別(沒有 __init__ 方法)內部,以 test 為前綴的測試函數或方法。以 @staticmethod@classmethods 修飾的方法也被考慮在內。

有關如何自訂測試探索的範例,請參閱 變更標準 (Python) 測試發現

在 Python 模組中,pytest 也使用標準的 unittest.TestCase 子類化技術來探索測試。

選擇測試佈局

pytest 支援兩種常見的測試佈局

應用程式程式碼外的測試

如果您有很多功能測試,或者由於其他原因想要將測試與實際應用程式程式碼分開(通常是個好主意),則將測試放入實際應用程式程式碼之外的額外目錄中可能很有用

pyproject.toml
src/
    mypkg/
        __init__.py
        app.py
        view.py
tests/
    test_app.py
    test_view.py
    ...

這具有以下優點

  • 您的測試可以在執行 pip install . 後針對已安裝的版本運行。

  • 您的測試可以在執行 pip install --editable . 後針對具有可編輯安裝的本地副本運行。

對於新專案,我們建議使用 importlib 導入模式(請參閱 which-import-mode 以獲得詳細說明)。為此,請將以下內容添加到您的 pyproject.toml

[tool.pytest.ini_options]
addopts = [
    "--import-mode=importlib",
]

通常,但特別是如果您使用預設導入模式 prepend強烈建議使用 src 佈局。在這裡,您的應用程式根套件位於您根目錄的子目錄中,即 src/mypkg/ 而不是 mypkg

這種佈局可以防止許多常見的陷阱,並且有很多好處,這些好處在 Ionel Cristian Mărieș 的這篇出色的 部落格文章 中得到了更好的解釋。

注意

如果您不使用可編輯安裝並且使用上面的 src 佈局,則需要擴展 Python 的模組檔案搜尋路徑,以便直接針對本地副本執行測試。您可以通過設定 PYTHONPATH 環境變數以特別的方式執行此操作

PYTHONPATH=src pytest

或以永久方式使用 pythonpath 配置變數,並將以下內容添加到您的 pyproject.toml

[tool.pytest.ini_options]
pythonpath = "src"

注意

如果您不使用可編輯安裝並且不使用 src 佈局(mypkg 直接在根目錄中),您可以依靠 Python 預設將當前目錄放入 sys.path 中的事實來導入您的套件並運行 python -m pytest 以直接針對本地副本執行測試。

有關調用 pytestpython -m pytest 之間的差異的更多信息,請參閱 調用 pytest 與 python -m pytest

作為應用程式程式碼一部分的測試

如果您在測試和應用程式模組之間有直接關係,並且想要將它們與您的應用程式一起分發,則將測試目錄內聯到您的應用程式套件中會很有用

pyproject.toml
[src/]mypkg/
    __init__.py
    app.py
    view.py
    tests/
        __init__.py
        test_app.py
        test_view.py
        ...

在這個方案中,可以使用 --pyargs 選項輕鬆運行您的測試

pytest --pyargs mypkg

pytest 將發現 mypkg 的安裝位置,並從那裡收集測試。

請注意,這種佈局也適用於上一節中提到的 src 佈局。

注意

您可以為您的應用程式使用命名空間套件 (PEP420),但 pytest 仍將根據 __init__.py 檔案的存在執行 測試套件名稱 探索。如果您使用上面兩種建議的檔案系統佈局之一,但從您的目錄中省略 __init__.py 檔案,它應該可以正常工作。但是,從「內聯測試」中,您將需要使用絕對導入來訪問您的應用程式程式碼。

注意

prependappend 導入模式下,如果 pytest 在遞迴到檔案系統時找到 "a/b/test_module.py" 測試檔案,它將按如下方式確定導入名稱

  • 確定 basedir:這是第一個「向上」(朝向根目錄)不包含 __init__.py 的目錄。例如,如果 ab 都包含 __init__.py 檔案,則 a 的父目錄將成為 basedir

  • 執行 sys.path.insert(0, basedir) 以使測試模組可以在完全限定的導入名稱下導入。

  • import a.b.test_module,其中路徑由將路徑分隔符 / 轉換為「.」字元確定。這意味著您必須遵循目錄和檔案名稱直接映射到導入名稱的慣例。

這種稍微演變的導入技術的原因是,在較大的專案中,多個測試模組可能會相互導入,因此導出規範的導入名稱有助於避免意外情況,例如測試模組被導入兩次。

使用 --import-mode=importlib,事情變得不那麼複雜,因為 pytest 不需要更改 sys.path,這使得事情變得不那麼令人意外。

選擇導入模式

由於歷史原因,pytest 預設為 prepend 導入模式,而不是我們為新專案推薦的 importlib 導入模式。原因在於 prepend 模式的工作方式

由於沒有套件可以從中導出完整的套件名稱,pytest 會將您的測試檔案作為頂層模組導入。第一個範例(src 佈局)中的測試檔案將通過將 tests/ 添加到 sys.path 中,作為頂層模組 test_apptest_view 導入。

與導入模式 importlib 相比,這會導致一個缺點:您的測試檔案必須具有唯一的名稱

如果您需要具有相同名稱的測試模組,作為一種解決方法,您可以將 __init__.py 檔案添加到您的 tests 資料夾和子資料夾中,將它們更改為套件

pyproject.toml
mypkg/
    ...
tests/
    __init__.py
    foo/
        __init__.py
        test_view.py
    bar/
        __init__.py
        test_view.py

現在 pytest 將模組作為 tests.foo.test_viewtests.bar.test_view 加載,允許您擁有相同名稱的模組。但是現在這引入了一個微妙的問題:為了從 tests 目錄加載測試模組,pytest 將儲存庫的根目錄前置到 sys.path,這增加了現在 mypkg 也可以導入的副作用。

如果您使用像 tox 這樣的工具在虛擬環境中測試您的套件,這會很成問題,因為您想要測試您的套件的已安裝版本,而不是儲存庫中的本地程式碼。

importlib 導入模式沒有上述任何缺點,因為導入測試模組時不會更改 sys.path

tox

完成工作並想要確保您的實際套件通過所有測試後,您可能需要研究虛擬環境測試自動化工具 toxtox 幫助您設定具有預定義依賴項的 virtualenv 環境,然後使用選項執行預先配置的測試命令。它將針對已安裝的套件而不是針對您的原始程式碼簽出運行測試,從而幫助檢測套件問題。

不要通過 setuptools 運行

不建議與 setuptools 集成,即您不應使用 python setup.py testpytest-runner,並且將來可能會停止工作。

由於它依賴於 setuptools 的已棄用功能,並且依賴於破壞 pip 中安全機制的功能,因此不建議使用此方法。例如,'setup_requires' 和 'tests_require' 會繞過 pip --require-hashes。有關更多資訊和遷移說明,請參閱 pytest-runner 通知。另請參閱 pypa/setuptools#1684

setuptools 打算 移除 test 命令

使用 flake8-pytest-style 檢查

為了確保在您的專案中正確使用 pytest,使用 flake8-pytest-style flake8 插件可能會有所幫助。

flake8-pytest-style 檢查 pytest 程式碼中的常見錯誤和編碼風格違規,例如 fixture 的不正確使用、測試函數名稱和標記。通過使用此插件,您可以在開發過程的早期捕獲這些錯誤,並確保您的 pytest 程式碼一致且易於維護。

可以在 flake8-pytest-style 的 PyPI 頁面 上找到 flake8-pytest-style 檢測到的 lint 列表。

注意

flake8-pytest-style 不是官方的 pytest 專案。某些規則強制執行某些風格選擇,例如使用 @pytest.fixture() 而不是 @pytest.fixture,但您可以配置插件以適合您偏好的風格。